CodeGrant
About 3192 wordsAbout 11 min
2025-03-07
This page describes the CodeGrant agreement-payment flow for selected local payment methods. The buyer completes a one-time wallet-side authorization, after which the merchant stores the resulting payment credential token and uses it within the agreed business scope for later charges without repeating the agreement flow or re-entering wallet account details on every payment.
This solution fits membership services, subscription renewals, recurring benefit fulfilment, auto-charge flows, and other “authorize first, fulfil later” business models. It is also suitable when the goal is to reduce repeat-payment friction and improve conversion for local-wallet scenarios.
Solution overview
CodeGrant can be viewed as a “local-wallet agreement token payment” capability with four core parts:
- Agreement setup: the merchant calls the bind API, receives
authUrl, and guides the buyer through the wallet-side agreement flow. - Credential storage: after success, the merchant stores the mapping among
token,merchantUserId, payment method, and agreement state. - Follow-up payment: the merchant server charges later with
token + bizType=CodeGrant. - Lifecycle management: notifications, query APIs, and unbind operations keep the token state in sync.
In PingPong Checkout, the typical implementation path is: the merchant first calls the Binding Interface to obtain authUrl and guide the buyer through the agreement flow; after agreement success, the merchant stores the token; later payments are created through Order Creation and Payment with token, bizType=CodeGrant, and merchantUserId. The final agreement result should be confirmed by the agreement-success notification or the agreement list query. The final payment result should be confirmed by the transaction notification or transaction query.
Supported payment methods
CodeGrant is only available for wallets or local payment methods that support this capability. For the current scope, see the CodeGrant payment-method list.
Interface list
CodeGrant integration usually involves the following APIs and notifications:
| Phase | Type | Document | Required | Purpose |
|---|---|---|---|---|
| Agreement | API | Binding Interface | Required | Get agreement URL |
| Notification | Agreement Success Notification | Required | Receive agreement result | |
| API | Binding List Interface | Recommended | Query agreement status | |
| Unbind | API | Unbind Interface | Required | Invalidate token |
| Notification | Agreement Cancellation Notification | Recommended | Sync unbind result | |
| Payment | API | Order Creation and Payment | Required | Initiate payment |
| Notification | Transaction Asynchronous Notification | Required | Receive payment result | |
| API | Transaction Query | Recommended | Query payment result | |
| Refund | API | Apply for Refund | As needed | Initiate refund |
| Notification | Refund Asynchronous Notification | Recommended | Receive refund result | |
| API | Refund Query | Recommended | Query refund result |
Integration flow
Flow summary:
- The buyer chooses a payment method that supports CodeGrant on the merchant page.
- The merchant frontend sends an agreement request to the merchant server.
- The merchant server calls the Binding Interface and returns
authUrlto the frontend. - The buyer is redirected to the wallet page and completes the agreement or authorization.
- PingPongCheckout pushes the agreement result through the agreement-success notification.
- The merchant stores the agreement result,
token,merchantUserId, and payment-method mapping. - For later charges, the merchant server calls Order Creation and Payment with the saved token.
- The merchant uses transaction notifications or transaction queries as the source of truth for payment results.
Integration checklist
Before integrating CodeGrant, complete the common setup in Prerequisites.
Also confirm the following CodeGrant-specific items:
- The target wallet or local payment method supports CodeGrant and has been enabled
- Publicly reachable
notificationUrlendpoints are ready for agreement and payment callbacks, andmerchantResultUrl/payResultUrlare prepared - A stable merchant-side
merchantUserIdis available and reused across bind, pay, and authorization cancellation - Your system can persist the mapping among
token, payment method, agreement status, and merchant user
Agreement integration
Complete the integration with the following steps:
Step 1: Create the agreement request
Call the Binding Interface from the merchant server. On success, PingPongCheckout returns an authUrl that the buyer must open to complete the wallet agreement flow.
Key request parameters:
| Parameter | Required | Description |
|---|---|---|
bizContent.requestId | M | Globally unique agreement request ID |
bizContent.merchantUserId | M | Merchant-side user ID that must remain the same for payment and authorization cancellation |
bizContent.paymentMethod.type | M | Payment-method type for this agreement |
bizContent.merchantResultUrl | M | Merchant page to return the buyer to after agreement completion |
bizContent.notificationUrl | M | Agreement-result notification endpoint |
bizContent.bizType | M | Fixed value CodeGrant |
bizContent.device.orderTerminal | M | Terminal type. 01 H5, 02 PC, 04 iOS App, 05 Android App |
Important
merchantResultUrl is only the buyer return page after agreement completion. It does not prove the agreement succeeded. Use the server-side notification or agreement query result as the source of truth.
Request example:
{
"accId": "2022102018024210348883",
"clientId": "2022102018024210348",
"signType": "SHA256",
"sign": "{{Sign}}",
"version": "1.0",
"bizContent": {
"requestId": "BIND_202606220001",
"merchantUserId": "MEMBER_10001",
"merchantResultUrl": "https://www.example.com/codegrant/result",
"notificationUrl": "https://www.example.com/codegrant/bind-notify",
"bizType": "CodeGrant",
"paymentMethod": {
"type": "AlipayHK"
},
"device": {
"orderTerminal": "02"
}
}
}Response key parameters:
| Field | Description |
|---|---|
bizContent.token | Business token associated with the agreement request |
bizContent.authUrl | Wallet agreement page URL that the frontend should open |
Response example:
{
"bizContent": "{\"token\":\"a15b5f3f79c739f39cc551273b542a90\",\"authUrl\":\"https://g.alipayplus.com/page/aplus-linker/acwallet/authorization.html?scopes=AGREEMENT_PAY%2CUSER_LOGIN_ID&bizContent=%7B%22acquirerId%22%3A%22Z02TE5520000000A%22%2C%22authClientDisplayName%22%3A%22ces%22%2C%22authClientId%22%3A%222023121216311410287%22%2C%22authClientName%22%3A%22xiaodian%22%2C%22authRedirectUrl%22%3A%22https%3A%2F%2Fg.alipayplus.com%2Fpage%2Fac-auth-payment%2Fresult%2Fmobile%2Findex.html%3FloadMode%3D2%26callbackType%3DCommon%26terminalType%3DWEB%26referenceAgreementId%3D17821172581855474%26authRequestId%3D2026062219091305000000002861103%26pspId%3D102216000000000000A%26clientId%3DT_4GGO000000000001%22%2C%22authState%22%3A%2217821172581855474%22%2C%22customerBelongsTo%22%3A%22ALIPAY_HK%22%2C%22pspId%22%3A%22102216000000000000A%22%2C%22referenceAgreementId%22%3A%2217821172581855474%22%2C%22referenceMerchantId%22%3A%222023121216311410287%22%2C%22scopes%22%3A%5B%22AGREEMENT_PAY%22%2C%22USER_LOGIN_ID%22%5D%2C%22terminalType%22%3A%22WEB%22%7D&source=AlipayConnect&needCallback=false\"}",
"sign": "08F61A05EA606424B8EA0F97AF2B0773CD3B082733A77A6AEF54BA38309D4572",
"signType": "SHA256"
}Important
Returning token and authUrl in the synchronous response does not mean the agreement is already effective. Only use the token for later payments after you receive the agreement-success notification or confirm status=ACTIVE through the Binding List Interface.
Step 2: Redirect the buyer to complete the agreement
After the merchant server receives authUrl, return it to the client and open it in the appropriate terminal environment.
function redirectToCodeGrantAuth(authUrl) {
if (!authUrl) return;
const authWindow = window.open(authUrl, '_blank');
if (!authWindow) {
window.location.assign(authUrl);
}
}function redirectToCodeGrantAuthOnWap(authUrl) {
if (!authUrl) return;
window.location.href = authUrl;
}NSURL *url = [NSURL URLWithString:authUrl];
if (url) {
[[UIApplication sharedApplication] openURL:url
options:@{}
completionHandler:^(BOOL success) {
if (!success) {
// Ask the buyer to retry or choose another payment method
}
}];
}try {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(authUrl));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
} catch (Exception e) {
e.printStackTrace();
}Step 3: Receive the agreement result and persist the token
After the buyer completes authorization, the merchant can confirm the agreement result through either of the following methods:
- Receive the agreement result pushed by PingPongCheckout to the
notificationUrlfrom the bind request. - Actively call
/v4/paymethod/bind/listto query the agreement result.
In actual integrations, asynchronous notifications are better suited for real-time result delivery, while the agreement query can be used both as an active confirmation method and for scenarios such as delayed notifications, callback-processing exceptions, and result verification. No matter which method is used, the final result should be based on the server-verified notification or query response.
Agreement notification request example:
{
"accId": "2023121216311410287522",
"bizContent": "{\"notifyType\":\"ACCESS_TOKEN_CREATION\",\"payMethod\":\"AlipayHK\",\"merchantUserId\":\"user00001\",\"token\":\"a15b5f3f79c739f39cc551273b542a90\"}",
"clientId": "2023121216311410287",
"sign": "0FFE63EE9CA214DA745131EDAAC15971C7087C9144E87017C4949F015EB55310",
"signType": "SHA256"
}After your service receives the notification, it should return HTTP 2xx first as the acknowledgement, and then execute the internal processing logic. There is no fixed response-body schema, and the following example shows a common response pattern:
HTTP/1.1 200 OK
Content-Type: text/plain; charset=UTF-8
Content-Length: 2
OKThe merchant can also actively call /v4/paymethod/bind/list based on its own system design to query the agreement status. This query can be used as a regular confirmation method, and it is also useful for cases such as delayed notifications, callback-processing exceptions, immediate verification after the buyer returns, or routine status checks. Do not treat the frontend return page or the bind API synchronous response as final proof of agreement success.
Agreement query key request parameters:
| Parameter | Description |
|---|---|
bizContent.token | Agreement token to confirm. Reuse the token returned by the bind API synchronous response |
bizContent.merchantUserId | Merchant-side user ID. Keep it consistent with the bind request and later payments |
bizContent.requestId | Query request ID. It should be globally unique for tracing and idempotent handling |
Agreement query request example:
{
"accId": "2022102018024210348883",
"clientId": "2022102018024210348",
"signType": "SHA256",
"sign": "{{Sign}}",
"version": "1.0",
"bizContent": {
"token": "a15b5f3f79c739f39cc551273b542a90",
"merchantUserId": "user00001",
"requestId": "2026062217440001"
}
}Agreement query response key parameters:
| Field | Description |
|---|---|
bizContent.bindInfos[].token | Agreement token returned by the query and expected to match the requested token |
bizContent.bindInfos[].merchantUserId | Merchant-side user ID bound to this token |
bizContent.bindInfos[].payMethod | Wallet or local payment method bound to the agreement |
bizContent.bindInfos[].status | Agreement status. ACTIVE means usable, INVALID means no longer usable |
Agreement query response example:
{
"bizContent": "{\"bindInfos\":[{\"bizType\":\"CodeGrant\",\"payMethod\":\"AlipayHK\",\"clientId\":\"2022102018024210348\",\"token\":\"a15b5f3f79c739f39cc551273b542a90\",\"accId\":\"2022102018024210348883\",\"merchantUserId\":\"user00001\",\"status\":\"ACTIVE\"}],\"managementURL\":\"https://sandbox-acquirer-static.pingpongx.com/payment/bindManage/index.html?code=aUW5WtMMzDltikAcGyKxBc746y8R9su6llj98pE6ut4hMDVf5kVZSkfpBqXh6Zi6EPyHdmsfROgdwb1bvp3tO91Me1qm7zfXFqOy7WcxRxOUfuSphTZuJ6MxmzxhzWW1jdgrdfNhDCSWxq_NsbKdVg==\"}",
"code": "001000",
"description": "Successful request",
"sign": "F253140644209B4A5527613FBD31056B8567584E9EF885CE1CB4A25625A14FAA",
"signType": "SHA256"
}Handling recommendations:
- If
bindInfoscontains the matching token andstatus=ACTIVE, mark the token as active and allow later payment or authorization-cancellation flows. - If
status=INVALID, mark the token as unusable and stop using it for later charges. - If no matching record is returned or the state is not yet usable, do not initiate payment yet. Retry the query after a short delay or wait for the agreement notification before confirming.
Follow-up payment integration
Step 4: Charge with the saved token
After the agreement succeeds, the merchant server can call Order Creation and Payment for follow-up charges. In CodeGrant mode, you usually do not need to collect account details again or display checkout again. The server only needs to pass the saved token, bizType=CodeGrant, and the same merchantUserId used during agreement creation.
Key payment parameters:
| Parameter | Required | Description |
|---|---|---|
bizContent.captureDelayHours | M | Fixed 0, meaning immediate capture |
bizContent.amount | M | Transaction amount |
bizContent.currency | M | ISO 4217 currency code |
bizContent.requestId | M | Globally unique payment request ID |
bizContent.merchantTransactionId | M | Merchant order ID |
bizContent.notificationUrl | M | Payment-result notification endpoint |
bizContent.payResultUrl | O | Buyer return page URL |
bizContent.shopperIP | M | Buyer IP address |
bizContent.token | M | Previously activated business token |
bizContent.bizType | M | Fixed CodeGrant |
bizContent.merchantUserId | M | Same merchant user ID used during agreement creation |
bizContent.paymentMethod.type | M | Wallet or local-payment method type bound to the token |
bizContent.customer.email | C | Buyer email. Required for physical-goods merchants and some payment methods |
bizContent.device.orderTerminal | M | Terminal type |
Request example:
{
"accId": "2023042011040310224447",
"clientId": "2023042011040310224",
"signType": "SHA256",
"sign": "{{Sign}}",
"version": "1.0",
"bizContent": {
"captureDelayHours": 0,
"amount": 100,
"currency": "HKD",
"requestId": "2026062313592001",
"payResultUrl": "https://www.example.com/codegrant/pay-result",
"notificationUrl": "https://www.example.com/codegrant/pay-notify",
"merchantTransactionId": "2026062313592001",
"shopperIP": "192.168.1.1",
"token": "502e597aa83bf423a3b8ec8484e7301f",
"bizType": "CodeGrant",
"merchantUserId": "user00001",
"paymentMethod": {
"type": "AlipayHK"
},
"customer": {
"email": "buyer@example.com"
},
"device": {
"orderTerminal": "02"
}
}
}Response example:
{
"accId": "2023042011040310224447",
"bizContent": "{\"transactionTime\":\"1782194370000\",\"requestId\":\"PMT-U6Q622OZ7I1782194369342\",\"merchantTransactionId\":\"2026062313592001\",\"action\":{\"type\":\"QR_CODE\"},\"currency\":\"HKD\",\"exchangedAmount\":\"100\",\"payResultUrl\":\"https://www.example.com/codegrant/pay-result\",\"exchangedCurrency\":\"HKD\",\"amount\":\"100\",\"transactionId\":\"26062300000450891549\",\"captureDelayHours\":0,\"status\":\"SUCCESS\"}",
"clientId": "2023042011040310224",
"code": "000000",
"description": "Transaction succeeded",
"sign": "4D294D16A4C91886992B630A1D48809749221CCFCF76D41E0BBADB0A709963FD",
"signType": "SHA256"
}Important
merchantUserId must remain identical to the value used in the agreement request. If it does not match, the token may be treated as non-existent, invalid, or unusable for this buyer.
Synchronous response status:
bizContent.status | Meaning | Handling recommendation |
|---|---|---|
SUCCESS | Synchronous response already succeeded | You may update the order state, but still verify final consistency through notification or query |
FAILED | Payment failed | Record the failure reason and let the buyer retry or use another payment method |
PROCESSING | Payment is still processing | Wait for the notification, and call Transaction Query if the result is delayed |
Step 5: Get the payment result
After payment processing, the merchant can obtain the payment result through either of the following methods:
- Receive the transaction result pushed by PingPongCheckout to the
notificationUrlfrom the payment request. - Actively call Transaction Query to query the transaction result.
Payment-result notification example:
{
"clientId": "2023042011040310224",
"code": "000000",
"bizContent": "{\"exchangedCurrency\":\"HKD\",\"amount\":\"100.000000\",\"transactionTime\":\"1782194370000\",\"transactionId\":\"26062300000450891549\",\"token\":\"502e597aa83bf423a3b8ec8484e7301f\",\"notifyType\":\"RECHARGE\",\"transactionEndTime\":\"1782194377229\",\"requestId\":\"PMT-U6Q622OZ7I1782194369342\",\"merchantTransactionId\":\"2026062313592001\",\"paymentMethod\":{\"type\":\"AlipayHK\"},\"currency\":\"HKD\",\"exchangedAmount\":\"100.000000\",\"captureDelayHours\":0,\"status\":\"SUCCESS\"}",
"sign": "8005BA10CFF17D377322423579D0EEBDAA6FA4EE99D4D3FE28CA0BE9D073E68E",
"accId": "2023042011040310224447",
"description": "Transaction succeeded",
"signType": "SHA256"
}If the result is received through an asynchronous notification, your service should return HTTP 2xx first to acknowledge receipt, and then execute the internal processing logic.
Notification response example:
HTTP/1.1 200 OK
Content-Type: text/plain; charset=UTF-8
Content-Length: 2
OKIf the notification is delayed, missed, or if the merchant wants to reconfirm the final status, it can actively call Transaction Query to query the transaction result. In most cases, merchantTransactionId can be used as the query condition.
Transaction query request example:
{
"accId": "2023042011040310224447",
"clientId": "2023042011040310224",
"signType": "SHA256",
"sign": "{{Sign}}",
"version": "1.0",
"bizContent": {
"merchantTransactionId": "2026062313592001"
}
}Transaction query response example:
{
"accId": "2023042011040310224447",
"bizContent": "{\"threeDSecure\":\"\",\"resultCode\":\"000000\",\"transactionTime\":\"1782194370000\",\"requestId\":\"PMT-U6Q622OZ7I1782194369342\",\"merchantTransactionId\":\"2026062313592001\",\"currency\":\"HKD\",\"exchangedAmount\":\"100.000000\",\"resultDescription\":\"Transaction succeeded\",\"exchangedCurrency\":\"HKD\",\"amount\":\"100.000000\",\"transactionEndingTime\":\"1782194377000\",\"transactionId\":\"26062300000450891549\",\"token\":\"502e597aa83bf423a3b8ec8484e7301f\",\"paymentMethod\":{\"type\":\"AlipayHK\"},\"captureDelayHours\":0,\"status\":\"SUCCESS\"}",
"clientId": "2023042011040310224",
"code": "001000",
"description": "Successful request",
"sign": "B2EC288714CFB3F1892ADADFF8C0053C02FE337BDB0F4F9276EC415E0D0DF6EA",
"signType": "SHA256"
}Query response key parameters:
| Field | Description |
|---|---|
bizContent.merchantTransactionId | Merchant order ID used to map the transaction back to the merchant order |
bizContent.transactionId | PingPong transaction ID |
bizContent.status | Transaction status that should be used together with server-side notification or query results to confirm the final state |
Important
Even if the buyer has already returned to payResultUrl, you should not directly treat the payment as successful. The server-side result must be the source of truth.
Cancel authorization
After authorization is completed, the merchant must provide a cancellation entry on the merchant page so the buyer can actively manage the established authorization relationship, for the following reasons:
- It ensures the buyer can manage the authorization relationship autonomously and cancel an existing authorization at any time based on account-security policies or actual usage needs.
- Some payment methods have system-level limits, and the same wallet account can usually keep only one or a small number of valid authorization credentials under the same merchant, so the merchant needs to support cancellation of historical authorizations.
Merchant-side cancellation
If the buyer cancels authorization within your application, you need to call the Unbind Interface to invalidate the payment-method token. In addition to the synchronous response, PingPong will also push an authorization-cancellation notification to the notificationUrl provided in this request so the final result can be confirmed. See the notification example below for the notification format.
Authorization-cancellation request key parameters:
| Parameter | Description |
|---|---|
bizContent.merchantUserId | Merchant-side user ID. It should stay consistent with the bind request and later payments |
bizContent.requestId | Authorization-cancellation request ID |
bizContent.token | Authorization token to invalidate |
bizContent.notificationUrl | Asynchronous notification endpoint for the authorization-cancellation result |
Authorization-cancellation request example:
{
"accId": "2023042011040310224447",
"clientId": "2023042011040310224",
"signType": "SHA256",
"sign": "{{Sign}}",
"version": "1.0",
"bizContent": {
"merchantUserId": "user00001",
"requestId": "202606230338001",
"token": "a15b5f3f79c739f39cc551273b542a90",
"notificationUrl": "https://www.example.com/codegrant/unbind-notify"
}
}Authorization-cancellation response key parameters:
| Field | Description |
|---|---|
bizContent.token | Token that has been cancelled |
bizContent.merchantUserId | Merchant-side user ID associated with the token |
bizContent.status | Cancellation result status. INVALID means the token has been invalidated |
Response example:
{
"bizContent": "{\"token\":\"a15b5f3f79c739f39cc551273b542a90\",\"merchantUserId\":\"user00001\",\"status\":\"INVALID\"}",
"code": "001000",
"description": "Successful request",
"sign": "03D06D5F9882B8D3410BB1622487ED63F73B0141ED9084823B492CCF903B43F4",
"signType": "SHA256"
}When bizContent.status=INVALID in the response, the authorization credential has become invalid and can no longer be used for later CodeGrant payments.
Payment-method-side cancellation
If the buyer cancels authorization on the payment-method side and the payment method supports authorization-cancellation notifications, you will also receive an authorization-cancellation notification sent by PingPong. PingPong sends this notification to the notificationUrl that was provided when creating the bind request through the Binding Interface; if the payment method does not support this capability, no such notification will be sent.
Authorization-cancellation notification example:
{
"accId": "2023042011040310224447",
"bizContent": "{\"notifyType\":\"ACCESS_TOKEN_CANCEL_OF_MERCHANT\",\"payMethod\":\"AlipayHK\",\"merchantUserId\":\"user00001\",\"token\":\"502e597aa83bf423a3b8ec8484e7301f\"}",
"clientId": "2023042011040310224",
"sign": "E5960BAAE68C98506318F8C67D27BD4A347F3236E42FACC28B6A52B77773F171",
"signType": "SHA256"
}After receiving the authorization-cancellation notification, your service should return HTTP 2xx first to acknowledge receipt, then execute the internal processing logic, and promptly mark the local token state as unusable.
Notification response example:
HTTP/1.1 200 OK
Content-Type: text/plain; charset=UTF-8
Content-Length: 2
OK