--- url: >- https://acquirer-api-docs-v4-en.pingpongx.com/en/notes/integrate/non-hosted-card/index.md description: >- International credit card payment (Non-hosted) is designed for merchants with PCI-DSS certification who build and operate their own card payment page. The merchant front end collects cardholder data and 3DS parameters, and the merchant server submits the payment request to PingPongCheckout. --- International credit card payment (Non-hosted) is designed for merchants with PCI-DSS certification who build and operate their own card payment page. The merchant front end collects cardholder data and integrates the risk control and 3DS components required by [Dynamic 3D Secure](/en/notes/risk/3ds/); the merchant server calls [Create Order and Pay](/en/notes/checkout/api/uniformly/) to create the payment order, and then decides whether to continue the 3DS challenge according to `status` and `threeDContinue` in the synchronous response. The final payment result must be confirmed by the merchant server through [Payment Notification](/en/notes/notify/payment/notify/) or [Transaction Query](/en/notes/checkout/api/getOne/). ## Solution Overview In this model, the shopper always enters card details on the merchant page. If 3DS is required, the merchant front end redirects the shopper to the issuer challenge page according to the synchronous response. This model is better suited to merchants that want a fully customized checkout experience and already have the PCI-DSS compliance and front-end payment capabilities required to operate it. ## Prerequisites 1. The merchant has PCI qualification and has been verified by PingPongCheckout. (For PCI-DSS, please send email attachments to `acquire-risk@pingpongx.com` and CC `gig-tech-acq@pingpongx.com`) 2. The merchant has the capability to develop its own credit card checkout page. 3. The merchant already has the front-end capability required to integrate the risk control and 3DS components. :::danger Note With the international credit card payment (Non-hosted) solution, the merchant must store and process cardholder data on its own side, so PCI-DSS certification is mandatory. ::: ## Interface List International credit card payment (Non-hosted) typically involves the following APIs and notifications:
Phase Type Document Required Purpose
Payment Creation Guide Dynamic 3D Secure Required reading Integrate the risk control and 3DS components and collect the required parameters for payment
API Create Order and Pay Required Submit card data, risk parameters, and order information to create the payment order
Payment Result Notification Payment Notification Required Receive the final payment result and update the merchant order status
API Transaction Query Recommended Actively confirm the transaction result when notifications are delayed, not received, or need verification
Refund API Refund Request As needed Initiate a refund for a successfully paid international card transaction
Notification Refund Notification Recommended Receive the refund result and update the refund status
API Refund Query Recommended Actively confirm the refund status when notifications are delayed, not received, or during reconciliation
Authorization Capture / Void API Authorization Capture Required for manual capture scenarios Capture a previously authorized transaction
API Authorization Void Required for manual capture scenarios Void a previously authorized but not yet captured transaction
Notification Capture Notification Recommended for manual capture scenarios Receive the capture result
Notification Void Notification Recommended for manual capture scenarios Receive the void result
## Interaction Flow ```mermaid %%{init: { 'theme': 'base', 'themeVariables': { 'primaryColor': '#E3F2FD', 'primaryTextColor': '#0D47A1', 'primaryBorderColor': '#1976D2', 'lineColor': '#1565C0', 'secondaryColor': '#BBDEFB', 'tertiaryColor': '#90CAF9', 'background': '#F8FBFF', 'mainBkg': '#E3F2FD', 'secondBkg': '#BBDEFB', 'tertiaryBkg': '#90CAF9', 'actorBkg': '#2196F3', 'actorBorder': '#1976D2', 'actorTextColor': '#FFFFFF', 'actorLineColor': '#1565C0', 'signalColor': '#0D47A1', 'signalTextColor': '#0D47A1', 'noteBkgColor': '#E1F5FE', 'noteTextColor': '#01579B', 'noteBorderColor': '#0288D1', 'loopTextColor': '#0D47A1', 'activationBkgColor': '#B3E5FC', 'activationBorderColor': '#0277BD' } }}%% sequenceDiagram participant U as 👤 Cardholder participant Client as 💻 Merchant Client participant SPG as 🛡️ SafePayGuardJs participant SSJ as 🔐 SecureShieldJs participant Server as 🏪 Merchant Server participant PP as 🔄 PingPong Server participant Issuer as 🏦 Issuer Note over U, Issuer: 🚀 Non-hosted Card Payment Flow Client->>SPG: 1. Initialize SafePayGuardJs Client->>SSJ: 2. Initialize SecureShieldJs U->>Client: 3. Enter card number Client->>SSJ: 4. triggerThreeDsInit (on card number change or blur) SSJ->>PP: 5. Validate and request risk parameters PP-->>SSJ: 6. Return part of jsGeneratedData / browserInfo U->>Client: 7. Click Pay Client->>SPG: 8. getGeneratedData() Client->>SSJ: 9. getGeneratedData() Client->>+Server: 10. Submit order + jsGeneratedData + browserInfo Server->>+PP: 11. Call Create Order and Pay PP->>+Issuer: 12. Request payment processing Issuer-->>-PP: 13. Return payment result PP-->>-Server: 14. Return synchronous response Server-->>-Client: 15. Return bizContent result alt threeDContinue = false (no 3DS required) Client->>U: 16. Show payment result else threeDContinue = true (3DS required) Client->>U: 17. Redirect to threeDRedirectUrl for challenge Note over U, PP: After the challenge, redirect to payResultUrl end Note over U, Issuer: 🎯 Flow Ends ``` Flow summary: 1. The merchant front end collects risk control and 3DS parameters before payment submission. 2. The merchant server calls [Create Order and Pay](/en/notes/checkout/api/uniformly/) to create the international card payment order. 3. After PingPongCheckout returns the synchronous response, the merchant front end decides whether to enter the 3DS challenge flow according to `status` and `threeDContinue`. 4. If 3DS is required, the merchant front end should redirect to `threeDUnionParams.threeDRedirectUrl`; after the challenge is completed, PingPongCheckout redirects the shopper back to `payResultUrl`. 5. The shopper returning to `payResultUrl` only means the front-end flow is finished. It does not mean the transaction succeeded. The final result must be confirmed through [Payment Notification](/en/notes/notify/payment/notify/) or [Transaction Query](/en/notes/checkout/api/getOne/). ## Integration Steps Complete the integration in the following steps: 1. [Prepare 3DS and risk parameters, then create the payment](#step-1-prepare-3ds-and-risk-parameters-then-create-the-payment) 2. [Get the final payment result](#step-2-get-the-final-payment-result) ### Step 1: Prepare 3DS and risk parameters, then create the payment Before calling [Create Order and Pay](/en/notes/checkout/api/uniformly/), the merchant front end must integrate SafePayGuardJs and SecureShieldJs and collect the required `browserInfo`, `jsGeneratedData`, and other risk-related parameters. The merchant server then calls Create Order and Pay to create the payment order. For component initialization, when to trigger `triggerThreeDsInit`, and how to obtain the generated parameters, refer to: - [3DS Integration Guide](/en/notes/risk/3ds/) - [SafePayGuardJs](/en/notes/risk/safePayGuardJs/) - [SecureShieldJs](/en/notes/risk/SecureShieldJs/) ::: warning Note If you cannot embed the required JS components into your payment page, first confirm with your business or risk contact whether another 3DS integration approach can be evaluated. ::: When creating the payment request, pay special attention to the following fields: | Field | Description | |:---|:---| | `paymentMethod.type` | Card brand or card scheme, such as `VISA`, `MasterCard`, `JCB`, `AmericanExpress`, `Discover`, `DinersClub`, and `UnionPay`. For supported card brands, see [International Cards](/en/notes/paymentMethods/InternationalCards/) | | `paymentMethod.cardInfo` | Cardholder card data, including card number, expiry date, CVV, and cardholder name | | `requestId` | Payment request ID. It should remain globally unique | | `merchantTransactionId` | Merchant order ID used to associate the merchant order with the payment transaction | | `merchantUserId` | Unique merchant-side user identifier used to associate the shopper identity | | `goods` | Goods information used to enrich transaction context | | `browserInfo` | Browser information collected by the 3DS component and submitted with the payment request | | `jsGeneratedData` | Security and risk parameters generated by the risk and 3DS components and submitted with the payment request | | `notificationUrl` | Asynchronous callback URL for the final payment result | | `payResultUrl` | URL where the shopper returns after completing the 3DS or payment flow | | `device.orderTerminal` | Order terminal. `01` WAP, `02` Web, `04` iOS App, `05` Android App | For full parameter details, see [Create Order and Pay](/en/notes/checkout/api/uniformly/). Payment request example (`bizContent` only): ```json { "captureDelayHours": "0", "amount": "10", "currency": "USD", "requestId": "PMT-E46D6LAAKB1782628205616", "merchantTransactionId": "PMT-E46D6LAAKB1782628205616", "payResultUrl": "https://www.example.com/card/pay-result", "payCancelUrl": "https://www.example.com/card/pay-cancel", "notificationUrl": "https://www.example.com/card/pay-notify", "language": "en", "shopperIP": "222.126.52.23", "merchantUserId": "888888888", "threeDSRequestData": { "executeThreeD": "Y" }, "customer": { "email": "buyer@example.com", "phone": "19999999999" }, "goods": [ { "name": "Product name mepsking1", "description": "sample description", "unitPrice": "10", "number": "1", "virtualProduct": "Y", "imgUrl": "https://yourOrderSample/001.png" } ], "device": { "orderTerminal": "02" }, "paymentMethod": { "type": "scheme", "cardInfo": { "number": "4242424242424242", "holderFirstName": "zhang", "holderLastName": "san", "expireMonth": "09", "expireYear": "2030", "cvv": "239" } }, "browserInfo": { "acceptHeader": "content-type=application/x-www-form-urlencoded;accept=*/*;cache-control=no-cache;postman-token=cf489264-5378-4d5d-a86c-72c86a408c03;accept-encoding=gzip, deflate;content-length=588;connection=keep-alive;", "colorDepth": "32", "jetLag": "480", "screenHeight": "1980", "screenWidth": "1080", "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36", "windowSize": "05" }, "jsGeneratedData": { "channel": "CyberSource", "version": "1.0", "correlationId": "af43fce6-70a9-4224-8583-bb106a96a12b", "threeDInitTransId": "0b13e799-b802-4c85-adcc-a8a738ee0381", "threeDSecureReferenceId": "af43fce6-70a9-4224-8583-bb106a96a12b" } } ``` ::: warning Note `payResultUrl` is only the return page after the shopper completes the front-end flow. It does not mean the payment has succeeded. For order finalization, fulfillment, or entitlement delivery, always rely on the server-side result. ::: After the payment request is submitted, the merchant server first receives a synchronous response. Synchronous response example (`bizContent` only): ```json { "issuerInfo": {}, "transactionTime": "1782466165000", "requestId": "PMT-WWK4L1L2Q11782466163266", "merchantTransactionId": "PMT-WWK4L1L2Q11782466163266", "currency": "USD", "exchangedAmount": "10", "exchangedCurrency": "USD", "amount": "10", "authenticationInfo": { "avsResult": "U", "cvvResult": "Y", "threeDSecure": "N" }, "cardInfo": { "isoCountryA2": "MY", "lastFourDigits": "1096", "cardLevel": "STANDARD", "paymentBrand": "Mastercard", "cardType": "CREDIT", "issuringBank": "PUBLIC BANK BERHAD", "ipCountry": "CN", "firstSixDigits": "520000", "isoCountry": "MALAYSIA" }, "transactionEndingTime": "1782466167761", "threeDUnionParams": { "threeDContinue": "false" }, "transactionId": "26062600000050931108", "threeDContinue": "false", "token": "919110fb49484e1760b3a206d26909bc4c42d09eef818d113e4b20a83cdc11fb", "paymentMethod": { "type": "scheme" }, "captureDelayHours": 0, "status": "SUCCESS" } ``` Key response fields: | Field | Description | |:---|:---| | `status` | Current transaction status, such as `SUCCESS`, `FAILED`, or `PROCESSING` | | `threeDContinue` | Whether the shopper must continue to the 3DS challenge flow | | `threeDUnionParams.threeDRedirectUrl` | Redirect URL used by the front end when a 3DS challenge is required | | `transactionId` | PingPong transaction ID | | `requestId` | Payment request ID | The authorization result should be determined based on `status`: `SUCCESS` and `FAILED` indicate final payment results, while `PROCESSING` indicates that the payment is still being processed. The table below shows the possible values of `status` in the synchronous response and the recommended follow-up actions: | `status` | Meaning | Next action | |:---|:---|:---| | `SUCCESS` | The authorization payment succeeds. | No further 3DS redirect is required. Save `transactionId`, then continue confirming the final payment result through the asynchronous notification or transaction query. | | `FAILED` | The authorization payment fails. | Ask the shopper to retry or use another card. | | `PROCESSING` | The transaction is being processed. | If `threeDUnionParams.threeDRedirectUrl` is returned, the shopper still needs to complete the 3DS challenge and the front end should redirect there when required. If no redirect URL is returned, continue waiting for the asynchronous notification or actively call the transaction query. | #### Redirect to the 3DS Authentication Page (Optional) This is an optional step and is required only when `threeDContinue=true` in the synchronous response. In that case, the shopper still needs to complete a 3DS challenge, and the synchronous response typically returns `threeDUnionParams.threeDRedirectUrl`, for example: ```json { "issuerInfo": {}, "transactionTime": "1782794168000", "requestId": "PMT-K9X4L2N7QV1782794167312", "merchantTransactionId": "PMT-K9X4L2N7QV1782794167312", "currency": "USD", "exchangedAmount": "10", "exchangedCurrency": "USD", "amount": "10", "authenticationInfo": { "avsResult": "Unknown", "cvvResult": "Y", "threeDSecure": "Y" }, "cardInfo": { "isoCountryA2": "US", "lastFourDigits": "1096", "cardLevel": "CLASSIC", "paymentBrand": "Mastercard", "cardType": "CREDIT", "issuringBank": "", "ipCountry": "CN", "firstSixDigits": "520000", "isoCountry": "UNITED STATES" }, "transactionEndingTime": "1782794170200", "threeDUnionParams": { "threeDContinue": "true", "threeDRedirectUrl": "https://sandbox-acquirer-static.pingpongx.com/payment/3ds-redirect.html?token=example" }, "transactionId": "26062900000150950001", "threeDContinue": "true", "paymentMethod": { "type": "scheme" }, "captureDelayHours": 0, "status": "PROCESSING" } ``` If the synchronous response indicates that 3DS is required, pass `threeDUnionParams.threeDRedirectUrl` directly to the front end and redirect the shopper to complete the challenge. ::: warning 3DS redirect notes - `threeDRedirectUrl` is the complete redirect URL returned by PingPongCheckout. The merchant front end should use the original value returned by the server directly and treat it as an opaque link. Do not append extra parameters or `decode` and then `encode` it again, otherwise the 3DS page may fail to open correctly. - Do not rely on fixed domains, fixed paths, or static rules to identify the 3DS challenge page. Always use the `threeDRedirectUrl` returned by the server. - Open the 3DS challenge in a top-level page. Do not host the challenge page inside an `iframe`, to avoid redirect, input, or return-page issues. - For apps, we recommend using the system browser or a system-level web container first. If you must use `WKWebView` or `WebView`, complete real-device testing in advance and confirm that redirect, back navigation, keyboard, and page scaling all behave correctly. ::: ::: tabs @tab Web ```js function redirectThreeDOnWeb(threeDRedirectUrl) { if (!threeDRedirectUrl) return; window.location.assign(threeDRedirectUrl); } ``` @tab WAP ```js function redirectThreeDOnWap(threeDRedirectUrl) { if (!threeDRedirectUrl) return; window.location.href = threeDRedirectUrl; } ``` @tab App (iOS) We recommend opening the 3DS page in the system browser: ```swift guard let url = URL(string: threeDRedirectUrl) else { return } UIApplication.shared.open(url, options: [:]) { success in if !success { // Ask the shopper to retry or use another card } } ``` If you must load the page in `WKWebView`, use the original URL returned by the server and make sure the URL is encoded only once. For example, if the URL already contains `%20`, do not encode it again into `%2520`. ```swift guard let url = URL(string: threeDRedirectUrl) else { return } let request = URLRequest(url: url) webView.load(request) ``` @tab App (Android) We recommend opening the 3DS page in the system browser or Chrome Custom Tabs: ```kotlin try { val intent = Intent(Intent.ACTION_VIEW, Uri.parse(threeDRedirectUrl)) intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) startActivity(intent) } catch (e: Exception) { e.printStackTrace() } ``` If you must load the page in `WebView`, enable at least the following settings. This helps when some issuer 3DS pages render with a desktop-like layout on Android phones: ```java WebSettings webSettings = webView.getSettings(); webSettings.setJavaScriptEnabled(true); webSettings.setLoadWithOverviewMode(true); webSettings.setUseWideViewPort(true); webView.loadUrl(threeDRedirectUrl); ``` ::: ::: tip Tip `threeDRedirectUrl` is a PingPongCheckout intermediate 3DS page. After the shopper opens it, the page continues the redirect to the issuer's 3DS challenge page. When the challenge is completed, PingPongCheckout redirects the shopper back to the `payResultUrl` configured in the payment request. ::: ### Step 2: Get the final payment result For international card payments, we recommend confirming the final result through server-side asynchronous notification first. Transaction query is better suited to active verification and follow-up checks. The final result of a card payment should preferably be confirmed through a server-side asynchronous notification. If the asynchronous notification is delayed, not delivered, handled abnormally, or the merchant needs to verify the result immediately after the shopper returns, the merchant can actively call Transaction Query in the After Payment section. For the full transaction query integration approach, see [After Payment > Transaction Query](#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 (`bizContent` only): ```json { "exchangedCurrency": "USD", "amount": "10.000000", "authenticationInfo": { "avsResult": "Unknown", "cvvResult": "Y", "threeDSecure": "Y" }, "cardInfo": { "firstName": "zhang", "isoCountryA2": "US", "lastName": "san", "lastFourDigits": "1096", "cardLevel": "CLASSIC", "paymentBrand": "Mastercard", "cardType": "CREDIT", "issuringBank": "", "ipCountry": "CN", "firstSixDigits": "520000", "isoCountry": "UNITED STATES" }, "issuerInfo": { "issuerResultMsg": "Successful approval/completion", "issuerResultCode": "00" }, "threeDSecure": "Y", "transactionTime": "1782794170000", "transactionId": "26062900000150950001", "notifyType": "RECHARGE", "requestId": "PMT-K9X4L2N7QV1782794167312", "merchantTransactionId": "PMT-K9X4L2N7QV1782794167312", "paymentMethod": { "type": "Mastercard" }, "currency": "USD", "exchangedAmount": "10.000000", "captureDelayHours": 0, "status": "SUCCESS" } ``` `status` in the payment notification indicates the final payment result, so the merchant can update the order based on `SUCCESS` or `FAILED`. Notification response example: ```http HTTP/1.1 200 OK Content-Type: text/plain; charset=UTF-8 Content-Length: 2 OK ``` ## After Payment The following sections describe how to actively query transaction status after card payment, and how to continue with Capture, Void, Refund, and Reconciliation. ### Transaction Query When the shopper returns from the payment page to the merchant result page, the asynchronous notification is delayed, not delivered, notification handling fails, or the merchant needs to run a compensation task, you can call [Transaction Query](/en/notes/checkout/api/getOne/) to get the latest transaction status. Transaction query request example (`bizContent` only): ```json { "merchantTransactionId": "PMT-WWK4L1L2Q11782466163266" } ``` Transaction query response example (`bizContent` only): ```json { "threeDSecure": "", "resultCode": "000000", "transactionTime": "1782466164000", "requestId": "PMT-WWK4L1L2Q11782466163266", "merchantTransactionId": "PMT-WWK4L1L2Q11782466163266", "currency": "USD", "exchangedAmount": "10.000000", "resultMessage": "Transaction succeeded", "exchangedCurrency": "USD", "amount": "10.000000", "authenticationInfo": { "avsResult": "U", "cvvResult": "Y", "threeDSecure": "N" }, "cardInfo": { "isoCountryA2": "MY", "lastFourDigits": "1096", "cardLevel": "STANDARD", "paymentBrand": "Mastercard", "cardType": "CREDIT", "issuringBank": "PUBLIC BANK BERHAD", "ipCountry": "CN", "firstSixDigits": "520000", "isoCountry": "MALAYSIA" }, "transactionEndingTime": "1782466168000", "transactionId": "26062600000050931108", "token": "919110fb49484e1760b3a206d26909bc4c42d09eef818d113e4b20a83cdc11fb", "paymentMethod": { "type": "scheme" }, "captureDelayHours": 0, "status": "SUCCESS" } ``` The authorization result should be determined based on `status`: `SUCCESS` and `FAILED` indicate final payment results, while `PROCESSING` indicates that the payment is still being processed. ### Capture If the original transaction uses manual Capture, refer to [Capture](/en/notes/onlinePayment/modify/Capture/) to complete the later capture step. ### Notifications For the payment result notification integration approach, refer to [Step 2: Get the final payment result](#step-2-get-the-final-payment-result) on this page. If you need Capture, Void, or Refund, the corresponding documents also include how to receive and confirm their result notifications. ### Void If the original transaction has been authorized but not yet captured, refer to [Void](/en/notes/onlinePayment/modify/Void/) to stop the later charge. ### 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](/en/notes/paymentMethods/overview/) 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](/en/notes/checkout/api/modifications/refund/) 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](/en/notes/onlinePayment/modify/Refund/) for the refund integration solution. For request parameters, response fields, and handling rules, see [Refund Request](/en/notes/checkout/api/modifications/refund/). To confirm the refund result, see [Refund Query](/en/notes/checkout/api/getRefund/). ### Reconciliation If you need to understand statement generation rules and settlement cycles, refer to [Settlement Cycle and Reconciliation Statement](/en/notes/reconciliation/settlementCycle/). If you need statement download capability, refer to [SFTP Service Application](/en/notes/reconciliation/transactionStatementDownload/).