Non-hosted Local Payment
About 2688 wordsAbout 9 min
2025-03-07
Non-hosted local payment is suitable for merchants who build their own checkout page. You need to display the available payment methods on the checkout page, let the merchant server call Create Order and Pay to create the payment order, and then continue the payment flow according to action in the response. The final payment result must be confirmed through Payment Notification or Transaction Query.
| Pay-in (Immediate Capture) | Authorization (Auth then Capture) | Refund & Partial Refund | 重新支付 | Token凭证支付 | 线下扫码支付 |
|---|---|---|---|---|---|
Solution Overview
In this model, the shopper starts payment on the merchant page. The merchant server calls Create Order and Pay to create the local payment order. When the response returns action, the merchant front end continues the shopper payment flow according to action. After the shopper completes payment, the merchant server confirms the final result through asynchronous notification or active query.
Payment Flow
Flow summary:
- The shopper chooses a local payment method on the merchant checkout page.
- The merchant front end submits order information, payment method, and device information to the merchant server.
- The merchant server calls Create Order and Pay to create the payment order.
- PingPongCheckout returns the synchronous response. If the shopper needs to continue, the response includes
action. - The merchant front end continues the shopper payment flow according to
action. The following steps use redirect-based local payment methods as the example. - After the shopper completes payment, PingPongCheckout pushes the payment result through
notificationUrl. - If the merchant does not receive the notification for a long time, it should actively call Transaction Query to confirm the final result.
Note
If the shopper returns from the local payment page to payResultUrl, it only means the shopper is back on the merchant page. It does not mean the payment has succeeded. Order posting, fulfillment, or entitlement delivery must rely on the server-side asynchronous notification or transaction query result.
API List
Non-hosted local payment typically involves the following APIs and notifications:
| Phase | Type | Document | Required | Purpose |
|---|---|---|---|---|
| Payment Creation | API | Create Order and Pay | Required | Create the local payment order and get payment continuation parameters |
| Payment Result | Notification | Payment Notification | Required | Receive the final payment result and update the merchant order status |
| Payment Result | API | Transaction Query | Recommended | Actively verify the transaction status when notifications are delayed, not received, or need fallback confirmation |
| Refund | API | Refund Request | As needed | Initiate a refund for a successful local payment transaction |
| Refund | Notification | Refund Notification | Recommended | Receive the refund result and update the refund status |
| Refund | API | Refund Query | Recommended | Actively confirm the refund status when notifications are delayed, not received, or during reconciliation |
Integration Preparation
Before you start development, make sure the following items are ready.
| Item | Description |
|---|---|
| Account information | clientId and accId have been obtained, and the shop status is confirmed to be able to transact |
| Payment method | The target local payment method has been enabled, and its supported countries, currencies, amount range, and refund capability have been confirmed |
| Signing capability | Request signing has been completed according to the Signature Guide, and all request parameters participate in signing |
| Signature verification capability | The merchant can verify the sign in PingPongCheckout responses and asynchronous notifications |
| Notification URL | A public notificationUrl is ready to receive asynchronous payment notifications |
| Result page URL | payResultUrl is ready for redirect-based payment methods to return the shopper to the merchant page |
| Order status handling | The merchant system stores merchantTransactionId, requestId, transactionId, and order status, and supports idempotent updates |
Tip
Different local payment methods may require extra fields such as customer, goods, billingAddress, shippingAddress, or a specific device.orderTerminal. Always follow the target payment method page and the Create Order and Pay parameter table.
Note
notificationUrl must be a complete public URL. Do not use localhost, 127.0.0.1, or an internal IP, and do not append query parameters to the URL.
Integration Steps
Complete the integration in the following steps:
- Step 1: Display available payment methods
- Step 2: Create the payment order
- Step 3: Continue the payment flow according to action
- Step 4: Get the final payment result
Step 1: Display available payment methods
Display the payment method name and identifier that you plan to support on the shopper checkout page, so the shopper can choose based on preference and scenario. The payment method list page should be implemented by your own front end.
When displaying payment methods, we recommend filtering at least by the following conditions:
| Condition | Handling recommendation |
|---|---|
| Trading country | Use the order country or shopper checkout country to match tradeCountry |
| Currency | Use the currency supported by the payment method. Refer to Transaction Currency for amount precision |
| Terminal | Prepare device.orderTerminal for Web, WAP, App, and other scenarios |
| Payment method type | Write the shopper-selected payment method into paymentMethod.type |
Step 2: Create the payment order
Call Create Order and Pay to create the payment order. You need to collect the shopper payment method, order information, device information, payment amount, and then submit the payment request from the merchant server.
Payment request key parameters:
| Parameter | Required | Description |
|---|---|---|
clientId | M | PingPong merchant ID |
accId | M | PingPong shop ID |
signType | M | Signature type, supports MD5 and SHA256 |
sign | M | Request signature |
version | M | API version, currently fixed at 1.0 |
bizContent.captureDelayHours | M | Local payment uses 0, meaning immediate capture |
bizContent.amount | M | Transaction amount |
bizContent.currency | M | ISO 4217 three-letter transaction currency |
bizContent.merchantTransactionId | M | Merchant order ID, unique order identifier |
bizContent.requestId | M | Payment request ID, globally unique |
bizContent.tradeCountry | M | Trading country, using ISO 3166-1 alpha-2 code |
bizContent.shopperIP | M | Shopper IP |
bizContent.paymentMethod.type | M | Local payment method selected by the shopper |
bizContent.goods | M | Goods information, including at least goods[].name, goods[].unitPrice, goods[].number, and goods[].imgUrl |
bizContent.device.orderTerminal | M | Order terminal. 01 mobile browser (WAP), 02 PC browser (WEB), 04 iOS App, 05 Android App |
bizContent.notificationUrl | O | Payment result notification URL, recommended |
bizContent.payResultUrl | C | Merchant result page URL for redirect-based local payment methods |
bizContent.merchantUserId | O | Merchant-side unique user ID |
bizContent.customer.email | C | Shopper email. Required for physical goods merchants; for virtual goods merchants, one of email or merchantUserId is required |
bizContent.customer.phone | C | Shopper phone number. Required by some local payment methods. Follow the specific payment method requirements |
For full parameter details, see Create Order and Pay.
Payment request example:
{
"accId": "2023042011040310224447",
"clientId": "2023042011040310224",
"signType": "SHA256",
"sign": "{{Sign}}",
"version": "1.0",
"bizContent": {
"captureDelayHours": 0,
"amount": "1000",
"currency": "THB",
"merchantTransactionId": "ORDER_202606100001",
"requestId": "REQ_202606100001",
"tradeCountry": "TH",
"shopperIP": "203.0.113.10",
"notificationUrl": "https://www.example.com/checkout/notify",
"payResultUrl": "https://www.example.com/checkout/result",
"paymentMethod": {
"type": "TrueMoney Wallet"
},
"customer": {
"email": "buyer@example.com",
"phone": "66123456789"
},
"goods": [
{
"name": "Digital product",
"description": "Order item",
"sku": "SKU_001",
"imgUrl": "https://www.example.com/product/SKU_001.png",
"unitPrice": "1000",
"number": "1",
"virtualProduct": "Y"
}
],
"device": {
"orderTerminal": "02"
}
}
}Tip
For readability, the examples in this guide expand bizContent as a JSON object. In real requests, process bizContent and the signature according to Create Order and Pay and the Signature Guide.
Response key fields:
| Parameter | Description |
|---|---|
code | Public response code. 000000 means the request is processed successfully |
description | Public response description |
bizContent.transactionId | PingPong transaction ID |
bizContent.merchantTransactionId | Merchant order ID |
bizContent.requestId | Payment request ID |
bizContent.status | Transaction status |
bizContent.action | Payment continuation parameters returned when the shopper needs to continue |
Response example:
{
"accId": "2023042011040310224447",
"clientId": "2023042011040310224",
"code": "000000",
"description": "Transaction succeeded",
"sign": "{{Sign}}",
"signType": "SHA256",
"bizContent": {
"transactionId": "2026061050010001",
"merchantTransactionId": "ORDER_202606100001",
"requestId": "REQ_202606100001",
"amount": "1000.000000",
"currency": "THB",
"status": "PROCESSING",
"paymentMethod": {
"type": "TrueMoney Wallet"
},
"action": {
"type": "PAYMENT_REDIRECT_URL",
"paymentRedirectUrl": "https://sandbox-acquirer-static.pingpongx.com/payment/apm.html?token=example"
},
"payResultUrl": "https://www.example.com/checkout/result",
"transactionTime": "1781080205000",
"captureDelayHours": 0
}
}bizContent.status in the synchronous response may return the following values:
bizContent.status | Meaning | Next action |
|---|---|---|
SUCCESS | The synchronous response already succeeded | The merchant server can update the order status, but we still recommend using asynchronous notification or query to ensure final consistency |
FAILED | Payment failed | Ask the shopper to choose another payment method or retry |
PROCESSING | Payment is in progress | If action is returned, continue with Step 3 and wait for the asynchronous notification or actively query the transaction |
Step 3: Continue the payment flow according to action
When bizContent.status in the synchronous response is PROCESSING and action is returned, the front end needs to continue the payment flow according to action. The following explanation uses redirect-based local payment methods where action.type=PAYMENT_REDIRECT_URL.
action.type | Key field | Front-end handling |
|---|---|---|
PAYMENT_REDIRECT_URL | action.paymentRedirectUrl | Redirect the shopper to this URL and let the shopper complete payment on the target payment page |
Payment continuation notes
action.paymentRedirectUrlis the complete payment continuation link returned by PingPongCheckout. The merchant front end should use the original value returned by the server directly and should not append extra parameters or replace the domain or path.- We recommend opening the payment continuation link in a top-level page, system browser, or system-level web container. If you must use
WKWebVieworWebView, complete real-device verification in advance.
After the merchant server receives the continuation link from PingPong, pass the URL to the front end and let the merchant front end redirect the shopper to the payment method page.
The following examples show how the merchant front end loads the payment continuation link:
function redirectOnWeb(action) {
if (action?.type !== 'PAYMENT_REDIRECT_URL') return;
const paymentWindow = window.open(action.paymentRedirectUrl, '_blank');
if (!paymentWindow) {
window.location.assign(action.paymentRedirectUrl);
}
}function redirectOnWap(action) {
if (action?.type !== 'PAYMENT_REDIRECT_URL') return;
window.location.href = action.paymentRedirectUrl;
}// Objective-C example:
NSURL *url = [NSURL URLWithString:paymentRedirectUrl];
if (url) {
[[UIApplication sharedApplication] openURL:url
options:@{}
completionHandler:^(BOOL success) {
if (!success) {
// Ask the shopper to retry or use another payment method
}
}];
}
// Swift example:
guard let url = URL(string: paymentRedirectUrl) else { return }
UIApplication.shared.open(url, options: [:]) { success in
if !success {
// Ask the shopper to retry or use another payment method
}
}// Java example:
try {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(paymentRedirectUrl));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
} catch (Exception e) {
e.printStackTrace();
}
// Kotlin example:
try {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(paymentRedirectUrl))
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(intent)
} catch (e: Exception) {
e.printStackTrace()
}Step 4: Get the final payment result
For local payments, we recommend confirming the final result through server-side asynchronous notification first. If the notification is delayed, not received, notification handling fails, or the merchant needs to verify the result immediately after the shopper returns, you can actively call transaction query. For the full transaction query integration solution, see After Payment > Transaction Query.
If you use the asynchronous notification flow, the merchant server should first return HTTP 2xx to acknowledge receipt, and then update the order according to the notification status.
Payment notification example:
{
"accId": "2023042011040310224447",
"clientId": "2023042011040310224",
"code": "000000",
"description": "Transaction succeeded",
"sign": "{{Sign}}",
"signType": "SHA256",
"bizContent": {
"amount": "1000.000000",
"currency": "THB",
"transactionId": "2026061050010001",
"merchantTransactionId": "ORDER_202606100001",
"requestId": "REQ_202606100001",
"notifyType": "RECHARGE",
"paymentMethod": {
"type": "TrueMoney Wallet"
},
"status": "SUCCESS",
"transactionTime": "1781080205000",
"transactionEndingTime": "1781080265000",
"captureDelayHours": 0
}
}Notification key fields:
| Parameter | Description | Handling recommendation |
|---|---|---|
sign / signType | Notification signature and signature type | Verify the signature before processing business logic |
bizContent.notifyType | Notification type. Payment notification is always RECHARGE | Used to identify the event type |
bizContent.transactionId | PingPong transaction ID | Save it for later query and reconciliation |
bizContent.merchantTransactionId | Merchant order ID | Used to locate the merchant order |
bizContent.requestId | Payment request ID | Used to locate the current payment request |
bizContent.amount / bizContent.currency | Transaction amount and currency | Must be checked against the merchant order amount and currency |
bizContent.status | Payment result status | Update the merchant order according to the status |
Recommended handling for bizContent.status in the notification:
bizContent.status | Handling recommendation |
|---|---|
SUCCESS | Payment succeeded. The merchant can fulfill the order or grant entitlements |
FAILED | Payment failed. Keep the order unpaid or failed and allow the shopper to retry |
CANCEL | Payment was cancelled or rejected by risk control. Do not treat the order as successful |
Notification response example:
HTTP/1.1 200 OK
Content-Type: text/plain; charset=UTF-8
Content-Length: 2
OKAfter Payment
Transaction Query
When the shopper returns from the payment page to the merchant result page, the asynchronous notification is delayed, notification handling fails, or the merchant needs to run a compensation task, you can call Transaction Query to get the latest transaction status.
Transaction query request example:
{
"accId": "2023042011040310224447",
"clientId": "2023042011040310224",
"signType": "SHA256",
"sign": "{{Sign}}",
"version": "1.0",
"bizContent": {
"merchantTransactionId": "ORDER_202606100001",
"requestId": "REQ_202606100001"
}
}Transaction query response example:
{
"accId": "2023042011040310224447",
"clientId": "2023042011040310224",
"code": "000000",
"description": "Transaction succeeded",
"sign": "{{Sign}}",
"signType": "SHA256",
"bizContent": {
"transactionId": "2026061050010001",
"merchantTransactionId": "ORDER_202606100001",
"requestId": "REQ_202606100001",
"amount": "1000.000000",
"currency": "THB",
"status": "SUCCESS",
"paymentMethod": {
"type": "TrueMoney Wallet"
},
"transactionTime": "1781080205000",
"transactionEndingTime": "1781080265000"
}
}Query response key fields:
| Parameter | Handling recommendation |
|---|---|
code | 000000 means the query request succeeded. A successful query request does not mean the payment succeeded |
bizContent.status | The merchant order status should follow this field |
bizContent.transactionId | Save the PingPong transaction ID for later refund, reconciliation, or query |
bizContent.amount / bizContent.currency | Check against the merchant order amount and currency |
bizContent.transactionEndingTime | Final transaction time, which can be used as the order completion time |
Recommended handling for transaction query statuses:
bizContent.status | Handling recommendation |
|---|---|
SUCCESS | Payment succeeded and the order can be completed |
FAILED | Payment failed. The shopper may be allowed to retry |
PROCESSING | Payment is still in progress. Continue waiting for the asynchronous notification or query again later |
CANCEL | Payment was cancelled or rejected by risk control. Do not treat it as successful |
CLOSED | The order has been closed and can no longer be paid |
Refund
Refund capability varies by payment method, including whether refunds are supported, the refund window, whether partial refunds are supported, and whether multiple refunds are supported. Refer to Payment Methods or the target payment method page to confirm the exact refund capability.
After a payment succeeds, if the shopper requests a refund, the merchant cancels a paid order, or part or all of the funds need to be returned, you can call Refund Request for the original transaction.
PingPongCheckout supports the following refund capabilities:
- Full refund
- Partial refund
- Multiple refunds, as long as the cumulative refund amount does not exceed the refundable amount of the original transaction
Refer to Refund for the refund integration solution. For request parameters, response fields, and handling rules, see Refund Request. To confirm the refund result, see Refund Query.
Reconciliation
To understand statement generation rules and settlement cycles, refer to Settlement Cycle and Reconciliation Statement. If you need statement download capability, refer to SFTP Service Application.
