---
title: 内嵌 SDK
permalink: /notes/zh/integrate/sdk-v4-preview/
createTime: '2026/07/09 10:00:00'
description: '以内嵌 SDK 为统一入口,通过 Web/WAP、iOS、Android 平台切换展示不同端的接入路径和完整指南入口。'
outline: [2, 4]
---
PingPong 内嵌 SDK 是基于 SDK 集成的支付组件,旨在帮助商户在自有 Web 页面或移动 App 内快速承载收银台能力,减少支付方式展示、支付交互和客户端适配成本,并为买家提供更连贯的支付体验。
- Web/WAP 端:适用于浏览器或 H5 页面环境,通过 JavaScript-SDK 在商户页面内渲染 PingPong 收银台。
- App 端(iOS / Android):适用于商户自有移动应用,通过 Native SDK 在 App 内唤起半屏收银台。
各端内嵌 SDK 支持的主要能力如下表所示:
| 特性 | 描述 | Web/WAP | iOS | Android |
|------|------|:-------:|:---:|:-------:|
| 支付方式列表渲染 | SDK 根据支付会话展示可用支付方式,商户无需自行维护支付方式列表和基础交互。 | 支持 | 支持 | 支持 |
| 站内嵌入体验 | 支付表单直接嵌入商户 Web/H5 页面,买家可在商户页面内完成支付操作。 | 支持 | 不支持 | 不支持 |
| 半屏收银台体验 | 在商户 App 内唤起 PingPong Native SDK 半屏支付面板,支付流程在 App 内完成。 | 不支持 | 支持 | 支持 |
| 界面与主题配置 | 支持按端侧能力配置收银台展示效果;Web/WAP 可通过 `customizeConfig`、`customizeStyles` 等配置布局、主题和交互。 | 支持 | 不支持 | 不支持 |
| 自定义支付按钮 | Web/WAP 可隐藏 SDK 原生支付按钮,并通过 `PingPong.Checkout.pay.run()` 由商户自定义按钮触发支付。 | 支持 | 不支持 | 不支持 |
| 支付结果回调 | Web/WAP 可通过 `checkoutResultHook` 接收前端支付结果;iOS / Android 可通过客户端回调接收结果。商户服务端仍应以支付通知或交易查询确认最终交易状态。 | 支持 | 支持 | 支持 |
## 平台选择
## Web/WAP
## 接入摘要
内嵌 SDK(预下单)是一种低代码站内收银台方案。商户服务端先调用 [prePay(预下单)接口](/notes/zh/checkout/api/reserve/) 创建支付会话,再由前端通过 JavaScript-SDK 在商户页面内直接渲染 PingPong 收银台。该方案既能保留买家在商户站内完成支付的体验,也能减少商户自建支付页、支付方式展示和支付交互处理的复杂度。
::: tip 适用场景
适合希望将收银台直接嵌入商户页面、降低支付页前端开发成本并保留站内支付体验的团队;若可接受买家跳转至 PingPong 托管收银台完成支付,建议选择[跳转收银台](/notes/zh/integrate/link/)。
:::
## 支付体验
### Web 支付
在内嵌SDK模式下,您可以将结账体验直接嵌入到自己的网站中。在网页端,结账页面会显示在您的网站内,使客户无需跳转至独立的托管页面即可完成支付。

### Mobile 支付
在内嵌SDK模式下,允许您将结账体验无缝集成至自有网站中。针对移动端小屏幕进行了专属 UI 优化,客户在手机网页或 App 内即可完成支付,全程无需跳转至外部托管页面,从而提供更流畅、沉浸的移动端支付体验。

## 支付流程
```mermaid
%% agent-tag: embedded-sdk-checkout-flow
sequenceDiagram
participant Cardholder as 🙋 持卡人
participant UserTerminal as 📱 用户终端
participant Merchant as 🏪 商户/建站平台
participant PP as 🔄 PingPongCheckout
participant Issuer as 🏦 ISSUER
Cardholder->>UserTerminal: 1. 发起结账
UserTerminal->>Merchant: 2. 提交订单
Merchant->>PP: 3. 请求下单接口(POST /v4/payment/prePay)
PP-->>Merchant: 4. 返回收银台参数(token / innerJsUrl / paymentUrl)
Merchant-->>UserTerminal: 5. 返回收银台初始化参数
UserTerminal->>PP: 6. 通过 JS-SDK 渲染收银台
PP-->>UserTerminal: 7. 展示收银台页面
Cardholder->>UserTerminal: 8. 选择支付方式并填写支付信息
UserTerminal->>PP: 9. 确认支付
alt 3D流程
PP-->>UserTerminal: 10. 跳转到 3D 挑战页
Cardholder->>UserTerminal: 11. 完成 3D 验证
UserTerminal->>PP: 12. 提交 3D 验证结果
PP->>Issuer: 13. issuer 验证数据
Issuer-->>PP: 14. 返回验证结果
PP-->>UserTerminal: 15. 触发 checkoutResultHook 或渲染支付结果页
UserTerminal->>Merchant: 16. 展示商户结果页 / 跳转 payResultUrl
Merchant-->>UserTerminal: 17. 根据查询/通知结果处理业务逻辑
else 非3D流程
PP-->>UserTerminal: 18. 触发 checkoutResultHook 或渲染支付结果页
UserTerminal->>Merchant: 19. 展示商户结果页 / 跳转 payResultUrl
Merchant-->>UserTerminal: 20. 根据查询/通知结果处理业务逻辑
end
PP->>Merchant: 21. 异步通知支付结果(notificationUrl)
Merchant-->>PP: 22. 返回 HTTP 200
```
::: steps
1. 买家下单
- 买家在商户页面发起结账并提交订单。
- 商户侧开始创建支付会话,准备初始化内嵌收银台。
2. 创建支付会话
- 商户服务端调用 [prePay(预下单)接口](/notes/zh/checkout/api/reserve/)。
- 获取 `token`、`innerJsUrl` 等收银台初始化所需参数。
3. 初始化并展示收银台
- 商户前端加载 JavaScript-SDK,并将 `accessToken` 与 `locale` 传给 `pp-checkout`。
- PingPong 收银台在商户页面内完成渲染并展示可用支付方式。
4. 买家完成支付
- 买家选择支付方式、填写支付信息并确认支付。
- 若触发 3D Secure,买家在当前支付流程中完成验证。
5. 确认支付结果
- 支付完成后,前端可通过 `checkoutResultHook` 接收 SDK 返回的前端支付结果并展示商户自有结果页;未配置 Hook 时,仍按原有结果页或 `payResultUrl` 逻辑处理。
- 商户服务端仍需结合异步通知或查询接口确认最终支付状态。
:::
::: warning 关键注意事项
1. 支付结果以异步通知(`notificationUrl`)或对应查询接口确认结果为准,`checkoutResultHook` 和前端结果页仅用于买家展示;
2. 优先使用 `prePay(预下单)接口` 返回的 `bizContent.innerJsUrl` 动态加载 SDK,以避免环境切换或版本变更带来的地址不一致问题;
3. `ready`、`error` 等事件监听器需要在设置 `accessToken` 之前注册,否则可能错过初始化事件;
4. 在 `locale` 未传入的情况下,收银台默认展示英文;如果传了 `locale`,则以传入值为准。具体枚举值可参考[语言列表](/notes/zh/appendix/languageCode/)。
:::
## 集成步骤
### 步骤 1:创建支付会话
在初始化 JS-SDK 收银台之前,商户服务端需要先调用 **prePay(预下单)接口** 获取 SDK 初始化所需的关键参数。
支付请求关键参数:
| 参数 | 必填 | 说明 |
|:---|:---:|:---|
| `captureDelayHours` | O | 资金请款方式。默认自动请款;如需手动请款,请按支付方式能力和接口规则配置 |
| `amount` | M | 交易金额 |
| `currency` | M | ISO 4217 三位交易币种 |
| `merchantTransactionId` | M | 商户网站订单流水号,订单唯一标识 |
| `shopperIP` | M | 买家下单 IP |
| `merchantUserId` | M | 商户侧用户唯一 ID |
| `goods` | M | 商品信息,至少包含商品名称、单价和数量 |
| `customer` | C | 买家信息。不同支付方式可能要求邮箱、手机号或账单信息,请以目标支付方式要求为准 |
| `paymentMethods` | O | 指定收银台可展示的支付方式范围,不传或为空时按店铺配置展示 |
| `notificationUrl` | O | 支付结果通知地址,建议传入 |
| `payResultUrl` | O | 买家完成支付流程后的商户结果页地址 |
| `language` | O | 收银台展示语言,未传入时默认展示英文,枚举值参考[语言列表](/notes/zh/appendix/languageCode/) |
有关完整参数的更多信息,请参阅 [prePay(预下单)接口](/notes/zh/checkout/api/reserve/)。
支付请求示例:
```json
{
"captureDelayHours": "0",
"amount": "100",
"currency": "USD",
"merchantTransactionId": "{{merchantTransactionId}}",
"payResultUrl": "https://test-acquirerpay.pingpongx.com/qa/result.html",
"notificationUrl": "https://test-acquirerpay.pingpongx.com/qa/result.html",
"language": "en",
"shopperIP": "222.126.52.23",
"merchantUserId": "USER_12345",
"customer": {
"email": "buyer@gmail.com"
},
"paymentMethods": [],
"goods": [
{
"name": "商品名称mepsking1",
"description": "商品描述",
"unitPrice": "1",
"number": "1",
"virtualProduct": "Y",
"imgUrl": "https://xiu.mepsking.top/material/1/16606169805015585.png"
}
]
}
```
支付响应示例:
```json
{
"amount": "1000",
"currency": "USD",
"transactionId": "2023092050004591",
"merchantTransactionId": "PMT-PR5GFUNZQP1695182276082",
"token": "EU:vr_YVR8u7rn7C1gG97DOg9_-Y66ubtNtoayJ_wiEEzdCnxCHYIk0pXordJYBjq1g",
"paymentUrl": "https://sandbox-acquirer-payment-ssr.pingpongx.com/v3/checkout?token=...",
"innerJsUrl": "https://paycdn.pingpongx.com/production/static/sdk/ppPay.min.js?token=..."
}
```
支付响应关键字段:
| 参数 | 说明 |
|:---|:---|
| `transactionId` | PingPong 交易流水号,用于后续查询、退款、对账 |
| `merchantTransactionId` | 商户网站订单流水号 |
| `token` | SDK 初始化令牌,用于设置 `pp-checkout` 的 `accessToken` 属性 |
| `innerJsUrl` | 动态加载 SDK 的 URL,推荐使用此地址加载 JavaScript-SDK |
| `paymentUrl` | 支付页面 URL;如业务后续切换为跳转收银台模式,可直接复用该地址 |
### 步骤 2:引入Javascript-SDK
复制以下代码,通过CDN地址引入 PingPongCheckout Javascript-SDK
::: code-tabs
@tab 🧪 沙箱环境
```js
```
@tab 🇪🇺 FRA 生产环境
```js
```
@tab 🇸🇬 SG 生产环境
```js
```
@tab 🇺🇸 US 生产环境
```js
```
:::
#### 动态获取 SDK 地址(推荐)
除了使用固定的 CDN 地址外,**推荐**从 `prePay(预下单)接口` 的响应中动态获取 SDK 地址。
调用 **prePay(预下单)接口**(`POST /v4/payment/prePay`)成功后,响应中会返回 `bizContent.innerJsUrl` 字段,该字段包含当前环境对应的 JS-SDK 地址。
```js:line-numbers title="src/utils/loadSDK.js"
async function initCheckoutWithDynamicSDK() {
// 1. 调用服务端 prePay(预下单)接口
const response = await fetch('/api/v4/payment/prePay', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
accId: '2018092714313010016291',
clientId: '2018092714313010016',
signType: 'SHA256',
version: '1.0',
bizContent: {
amount: '100',
currency: 'USD',
merchantTransactionId: 'ORDER_' + Date.now(),
notificationUrl: 'https://your-domain.com/notify',
payResultUrl: 'https://your-domain.com/result.html',
customer: {
email: 'customer@example.com',
firstName: 'John',
lastName: 'Doe'
}
}
})
});
const data = await response.json();
// 检查响应状态
if (data.code !== '000000') {
console.error('预下单失败:', data.description); // [!code error]
return;
}
const { token, innerJsUrl } = data.bizContent;
// 2. 动态加载 JS-SDK
await loadScript(innerJsUrl); // [!code focus]
// 3. 配置收银台
const ppCheckout = document.querySelector('pp-checkout');
ppCheckout.setAttribute('accessToken', token);
ppCheckout.setAttribute('locale', 'en');
}
/**
* 动态加载脚本
* @param {string} src - 脚本地址
* @returns {Promise}
*/
function loadScript(src) {
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.type = 'module';
script.src = src;
script.onload = resolve;
script.onerror = reject;
document.head.appendChild(script);
});
}
initCheckoutWithDynamicSDK();
```
::: tip 提示
使用动态获取的 SDK 地址可以确保始终使用与当前环境匹配的最新版本,无需在代码中硬编码环境地址。
:::
### 步骤 3:初始化并渲染收银台
::: note 提示
从沙箱环境切换到生产环境时,请务必检查并且完成下列的操作,否则会导致收银台无法正常渲染。
- 将引入Javascript-SDK的CDN地址切换到生产环境指定的URL
:::
1. 当你在联调**沙箱环境**时,需要在引入**沙箱环境** 的PingPongCheckout Javascript-SDK地址(发布到生产环境的时候别忘了切换成**生产环境**的地址)
2. 将 `pp-checkout` 标签插入 html body 中
```html:line-numbers title="index.html"
```
3. 将预下单获取到的 `accessToken` 传入,接口文档详见prePay(预下单)接口
```html:line-numbers title="index.html"
```
4. 你可以将收银台所要展示的语种(默认为英文,更多语言详见Locale)通过标签属性的方式传递给 `pp-checkout`,如下:
```html:line-numbers title="index.html"
```
通过以上步骤,你已经成功渲染了 Javascript-SDK 收银台。
### 步骤 4:收银台行为与界面配置
在使用全局变量前,请确保Javascript-SDK 加载完成。
#### customizeConfig 布局与界面配置
通过 `PingPong.Checkout.customizeConfig` 可以自定义收银台的布局和界面元素:
| 配置项 | 类型 | 默认值 | 说明 |
|-------|------|-------|------|
| `layout` | `'tab'\|'accordion'` | `'tab'` | 页面布局样式,可选值:`"tab"`(标签页样式)、`"accordion"`(平铺样式) |
| `displayCheckoutHeader` | `boolean` | `true` | 是否显示收银台顶部标题栏 |
| `originalPay` | `boolean` | `true` | 是否显示原生支付按钮 |
| `toPingPongResult` | `boolean` | `true` | 支付完成后是否跳转到 PingPong 结果页。`false` 表示直接跳转到商户配置的结果页面 |
| `hideStoredCards` | `boolean` | `false` | 是否隐藏 COF(Card On File)列表 |
| `onlyDisplaySavedCard` | `boolean` | `false` | 设为 `true` 时,收银台仅展示已存储的卡信息区域(隐藏新卡输入)。适用于 [复购 CVV 收集收银台](/notes/zh/integrate/tokenization/cardOnFileCVV/) |
| `disableCardRemoval` | `boolean` | `false` | 设为 `true` 时,禁止用户删除已存储的卡信息。适用于 [复购 CVV 收集收银台](/notes/zh/integrate/tokenization/cardOnFileCVV/) |
| `displayCardPrompt` | `boolean` | `true` | 是否显示卡支付提示信息 |
| `localizationErrorMsg` | `boolean` | `false` | 是否翻译支付错误消息 |
| `displayCardsLogo` | `boolean` | `true` | 是否显示卡品牌 logo 列表 |
| `isChallengeIframe` | `boolean` | `false` | 是否使用 iframe 展示 3DS Challenge 页面。设为 `true` 时,将通过 iframe 承载 Challenge 流程 |
::: warning 注意
`toPingPongResult` 配置需要在**服务端创建支付会话时**设置。客户端设置可能会被服务端配置覆盖。如需禁用 PingPong 结果页跳转,请确保服务端已正确配置。
:::
```js:line-numbers title="src/config/payment.js"
// [!code highlight:1] customizeConfig 布局与界面配置
PingPong.Checkout.customizeConfig = { // [!code focus]
layout: "accordion", // 平铺样式
displayCheckoutHeader: false, // 隐藏标题栏
originalPay: false, // 隐藏原生支付按钮,需自定义按钮触发支付
toPingPongResult: false, // 支付完成后不跳转到 PingPong 结果页
hideStoredCards: false, // 显示已保存的卡列表
displayCardPrompt: true, // 显示卡支付提示信息
localizationErrorMsg: false, // 不翻译错误消息
displayCardsLogo: true, // 显示卡品牌 logo
isChallengeIframe: true // 使用 iframe 展示 3DS Challenge 页面
};
```
##### 自定义支付按钮(可选)
当 `originalPay` 设置为 `false` 时,需要自定义按钮点击事件来触发支付。
```js:line-numbers title="src/config/payment.js"
// 初始化参数中 originalPay 为 false 时, 需自定义支付按钮点击事件
document.querySelector('#pay').onclick = function () { // [!code focus]
PingPong.Checkout.pay.run() // [!code focus]
}
```
#### customizeStyles 主题样式
通过 `PingPong.Checkout.customizeStyles` 可以自定义收银台的视觉样式:
| 配置项 | 类型 | 说明 | 示例值 |
|-------|------|------|-------|
| `themeColor` | `string` | 主题色,支持 CSS 颜色值 | `"rgb(26, 29, 37)"`, `"#1a1d25"` |
| `themeColorLight` | `string` | 主题色浅色变体 | `"rgba(26, 29, 37, 0.4)"` |
| `fontFamily` | `string` | 字体系列 | `"'Montserrat', sans-serif"` |
```js:line-numbers title="src/config/styles.js"
// [!code highlight] 品牌定制:深色主题
PingPong.Checkout.customizeStyles = {
themeColor: "rgb(26, 29, 37)", // 主题色
themeColorLight: "rgba(26, 29, 37, 0.4)", // 主题色浅色变体
fontFamily: "'Montserrat', 'PingFang SC', 'Microsoft YaHei', sans-serif"
};
```
::: tip 提示
主题色和字体配置需在 SDK 加载完成后、设置 `accessToken` 之前进行。
:::
#### PingPong.Checkout.beforeCheckoutHook
type:
```js
(() => void) | (() => Promise)
```
`beforeCheckoutHook` 用来设置发起支付请求前的钩子函数。
当你在用户点击支付按钮,发起支付请求前,需要执行你自己的业务逻辑,如:上报埋点、检查库存等,可以设置该钩子函数。
该函数可以返回一个`Promise`,后续的支付流程会等待该 Promise 状态变为 Fulfilled 后才会继续执行。如果你想在 Promise 状态为 Rejected 或者异步结果不满足你的业务条件时,可以抛出异常,SDK在捕获到异常后中断支付流程。
```js:line-numbers title="src/hooks/beforeCheckout.js"
// [!code highlight] 支付前钩子:检查库存
PingPong.Checkout.beforeCheckoutHook = () => { // [!code focus]
return fetch('/api/requestInventory').then(res => { // [!code focus]
const { inventoryQuantity } = res;
if(inventoryQuantity < MIN_QUANTITY) {
throw new Error('库存不足,需中断交易') // [!code error]
}
}).catch((error) => {
throw new Error('接口异常,需中断交易') // [!code error]
})
};
```
#### PingPong.Checkout.checkoutResultHook
type:
```ts
(payload: CheckoutResultHookPayload) => void | Promise
```
`checkoutResultHook` 用于接收 SDK 前端可直接得到的支付结果,并由商户前端接管支付完成后的页面展示或跳转。请在用户发起支付前注册该 Hook。该 Hook 挂载在 `PingPong.Checkout` 上,不是 `` 标签属性。
`checkoutResultHook` 会把支付结果统一收敛为以下三种状态:
| 状态 | 含义 | 建议处理 |
|:---|:---|:---|
| `success` | 支付成功 | 展示商户成功页,或跳转到商户成功页 |
| `pending` | 支付处理中或待确认,包含 SDK 内部的 `PENDING`、`PROCESSING`、`REVIEW` 等非终态场景 | 展示处理中页面,并通过服务端查询或异步通知确认最终结果 |
| `failed` | 支付失败 | 展示失败页,提供重试或返回商户站点的入口 |
Payload 类型定义:
```ts:line-numbers title="src/types/checkout-result.ts"
type CheckoutResultHookPayload = {
status: 'success' | 'pending' | 'failed';
code: string;
message: string;
merchantTransactionId?: string;
paymentMethod?: string;
};
```
Payload 字段说明:
| 字段 | 类型 | 是否必返 | 说明 |
|:---|:---|:---:|:---|
| `status` | `success` \| `pending` \| `failed` | M | SDK 归一化后的前端支付结果 |
| `code` | `string` | M | 支付结果码或错误码;无特定错误时可能返回 `0` |
| `message` | `string` | M | 支付结果描述或错误描述 |
| `merchantTransactionId` | `string` | O | 商户订单号 |
| `paymentMethod` | `string` | O | 支付方式 |
Payload 示例:
```json
{
"status": "pending",
"code": "0",
"message": "Payment is processing",
"merchantTransactionId": "ORDER_202607200001",
"paymentMethod": "VISA"
}
```
接入示例:
```js:line-numbers title="src/hooks/checkoutResult.js"
PingPong.Checkout.checkoutResultHook = async (payload) => { // [!code focus]
const orderNo = encodeURIComponent(payload.merchantTransactionId || '');
switch (payload.status) {
case 'success':
window.location.href = `/payment/success?orderNo=${orderNo}`;
break;
case 'pending':
window.location.href = `/payment/pending?orderNo=${orderNo}`;
break;
case 'failed':
window.location.href = `/payment/failed?code=${encodeURIComponent(payload.code)}`;
break;
default:
window.location.href = `/payment/result?orderNo=${orderNo}`;
break;
}
};
```
触发与跳转规则:
| 商户配置 | `success` / `pending` 场景 | `failed` 场景 | 结果页跳转 |
|:---|:---|:---|:---|
| 未配置 Hook | 不触发 Hook | 不触发 Hook | 保持原有结果页跳转逻辑 |
| 仅配置 `checkoutResultHook` | 触发 `checkoutResultHook` | 触发 `checkoutResultHook` | 命中 Hook 后不再跳转 PingPong 结果页 |
| 仅配置 `checkoutFailedHook` | 不触发 Hook | 触发 `checkoutFailedHook` | 保持原有失败处理逻辑 |
| 同时配置两个 Hook | 触发 `checkoutResultHook` | 优先触发 `checkoutFailedHook` | 失败场景不重复回调 |
建议新接入商户优先配置 `checkoutResultHook`。如果历史上已经接入 `checkoutFailedHook`,请在确认业务兼容后再迁移到 `checkoutResultHook`,避免同一失败结果被重复处理。
::: warning 注意
- `checkoutResultHook` 是浏览器侧回调,不能作为商户后台更新订单状态的唯一依据。最终支付结果请以 PingPong 服务端异步通知或订单查询接口为准。
- 如果同时配置 `checkoutResultHook` 和 `checkoutFailedHook`,支付失败场景会优先触发 `checkoutFailedHook`,SDK 不会再重复触发 `checkoutResultHook`。如需由一个 Hook 统一处理 `success`、`pending`、`failed`,请只配置 `checkoutResultHook`。
- `pending` 不是失败。请展示处理中状态,并在商户后台继续等待最终支付结果。
- Hook 支持返回 `Promise`,SDK 会等待 Promise 完成。请避免在 Hook 内执行长时间阻塞逻辑。
- Hook 内部异常不会恢复 PingPong 结果页跳转。商户应在 Hook 内自行处理异常,并准备兜底页面。
- 需要跳转到 3DS 认证页面,或渠道返回 `redirectUrl`、中间页地址时,SDK 仍会继续执行原有跳转流程。
:::
#### PingPong.Checkout.checkoutFailedHook
type:
```js
(() => void) | (() => Promise)
```
checkoutFailedHook 接收以下参数:
```js
(code: string, message: string) => void | Promise;
// code: string - 错误码
// message: string - 错误消息
```
`checkoutFailedHook` 用来自定义错误逻辑
当用户支付失败时,PingPong 默认会弹窗提示用户失败原因。如果你想自定义弹窗 UI 或文本,可以设置该钩子函数。
该函数可以返回一个 Promise。如果返回 Promise,后续的流程会等待该 Promise 状态变为 Fulfilled 后才继续执行
```js:line-numbers title="src/hooks/checkoutFailed.js"
// [!code highlight] 支付失败钩子:自定义错误提示
PingPong.Checkout.checkoutFailedHook = (code: string, message: string) => { // [!code focus]
notification.open({ // [!code focus]
message: 'Error title',
description: `${code}: ${message}` // [!code warning]
})
};
```
#### 事件监听(可选)
SDK 支持监听初始化过程中的 `ready` 和 `error` 事件,方便外部进行状态管理和错误处理。
```js:line-numbers title="src/config/payment.js"
// 监听 SDK 初始化事件
// 监听初始化成功事件
document.querySelector('pp-checkout').addEventListener('ready', (e) => {
console.log('SDK初始化成功'); // 初始化成功回调
// 可以在此处执行初始化完成后的逻辑,如隐藏加载动画
});
// 监听初始化失败事件
document.querySelector('pp-checkout').addEventListener('error', (e) => {
console.log('SDK初始化失败', e.detail); // 错误信息
// 可以在此处执行错误处理逻辑,如显示错误提示、重试等
});
```
**事件说明:**
- `ready`: 当 SDK 初始化成功时触发,表示收银台已准备就绪。`event.detail` 包含 SDK 实例信息
- `error`: 当 SDK 初始化失败时触发,如 accessToken 无效、网络错误等。`event.detail` 包含 `{ message, code }` 错误信息
::: warning 注意
事件监听器必须在 `setAttribute` 之前添加,否则可能无法捕获事件。
```js:line-numbers title="src/config/payment.js"
// ❌ 错误:先设置属性再添加监听器
ppCheckout.setAttribute('accessToken', token);
ppCheckout.addEventListener('ready', handler); // 可能无法触发! // [!code error]
// ✅ 正确:先添加监听器再设置属性
ppCheckout.addEventListener('ready', handler); // [!code focus]
ppCheckout.setAttribute('accessToken', token);
```
:::
## 使用示例
**原生 JavaScript 完整示例**
以下示例展示了如何在原生 JavaScript 项目中集成 SDK,包含完整的项目结构、API 调用和错误处理。
::: code-tree title="原生 JavaScript 集成示例" height="600px" entry="index.html"
```html:line-numbers title="index.html" :active
PingPong Checkout SDK - 原生 JS 示例
```
```css:line-numbers title="styles.css"
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
background: #f5f5f5;
padding: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
background: white;
padding: 30px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
h1 {
color: #333;
margin-bottom: 30px;
text-align: center;
}
.loading {
text-align: center;
padding: 40px;
}
.spinner {
width: 40px;
height: 40px;
margin: 0 auto 20px;
border: 4px solid #f3f3f3;
border-top: 4px solid #1890ff;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.error {
padding: 20px;
background: #fff2f0;
border: 1px solid #ffccc7;
border-radius: 4px;
color: #ff4d4f;
text-align: center;
}
.error button {
margin-top: 15px;
padding: 8px 20px;
background: #1890ff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
.error button:hover {
background: #40a9ff;
}
#checkout-wrap {
display: none;
}
```
```js:line-numbers title="config.js"
// SDK 配置
const CONFIG = {
// API 端点配置
apiEndpoint: '/api/reserve', // [!code focus]
// SDK CDN 地址(根据环境切换)
sdkUrl: {
sandbox: 'https://payssr-cdn.pingpongx.com/production-fra/acquirer-checkout-web/sandbox/pp-checkout.js',
production: 'https://payssr-cdn.pingpongx.com/production-fra/acquirer-checkout-web/pp-checkout.js'
},
// 默认语言
defaultLocale: 'zh', // [!code focus]
// 请求超时时间(毫秒)
timeout: 30000,
// 最小库存数量(用于演示 beforeCheckoutHook)
minInventory: 1
};
```
```js:line-numbers title="api.js"
// API 调用封装
const API = {
/**
* 获取 AccessToken
* @returns {Promise} AccessToken
*/
async getAccessToken() { // [!code focus]
try {
const response = await fetch(CONFIG.apiEndpoint, { // [!code focus]
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
// 订单信息
amount: 100.00,
currency: 'USD',
// ... 其他必要参数
})
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`); // [!code warning]
}
const data = await response.json();
if (!data.accessToken) {
throw new Error('响应中缺少 accessToken'); // [!code error]
}
return data.accessToken; // [!code focus]
} catch (error) {
console.error('获取 AccessToken 失败:', error);
throw error;
}
},
/**
* 检查库存(示例)
* @returns {Promise<{inventoryQuantity: number}>}
*/
async checkInventory() {
// 模拟 API 调用
return new Promise((resolve) => {
setTimeout(() => {
resolve({ inventoryQuantity: 10 });
}, 500);
});
}
};
```
```js:line-numbers title="main.js"
// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', async () => {
await initCheckout(); // [!code focus]
});
/**
* 初始化收银台
*/
async function initCheckout() {
const loadingEl = document.getElementById('loading');
const errorEl = document.getElementById('error');
const errorMessageEl = document.getElementById('error-message');
const checkoutWrap = document.getElementById('checkout-wrap');
const ppCheckout = document.querySelector('pp-checkout');
try {
// 1. 等待 SDK 加载完成
await waitForSDK(); // [!code focus]
// 2. 配置 SDK hooks
setupHooks(); // [!code focus]
// 3. 获取 AccessToken
const accessToken = await API.getAccessToken(); // [!code focus]
// 4. 设置 AccessToken 到 SDK
ppCheckout.setAttribute('accessToken', accessToken); // [!code focus]
ppCheckout.setAttribute('locale', CONFIG.defaultLocale);
// 5. 显示收银台
loadingEl.style.display = 'none';
checkoutWrap.style.display = 'block';
console.log('收银台初始化成功');
} catch (error) {
console.error('初始化失败:', error);
loadingEl.style.display = 'none';
errorMessageEl.textContent = `初始化失败: ${error.message}`;
errorEl.style.display = 'block';
}
}
/**
* 等待 SDK 加载完成
*/
function waitForSDK() {
return new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
reject(new Error('SDK 加载超时'));
}, CONFIG.timeout);
const checkSDK = () => {
if (window.PingPong && window.PingPong.Checkout) { // [!code focus]
clearTimeout(timeout);
resolve();
} else {
setTimeout(checkSDK, 100);
}
};
checkSDK();
});
}
/**
* 配置 SDK Hooks
*/
function setupHooks() {
// 支付前钩子:检查库存
PingPong.Checkout.beforeCheckoutHook = async () => { // [!code focus]
try {
const { inventoryQuantity } = await API.checkInventory();
if (inventoryQuantity < CONFIG.minInventory) {
throw new Error('库存不足,无法完成支付'); // [!code error]
}
console.log('库存检查通过,库存数量:', inventoryQuantity);
} catch (error) {
console.error('库存检查失败:', error);
throw error;
}
};
// 支付结果钩子:接管支付完成后的页面展示或跳转
PingPong.Checkout.checkoutResultHook = async (payload) => { // [!code focus]
const orderNo = encodeURIComponent(payload.merchantTransactionId || '');
if (payload.status === 'success') {
window.location.href = `/payment/success?orderNo=${orderNo}`;
return;
}
if (payload.status === 'pending') {
window.location.href = `/payment/pending?orderNo=${orderNo}`;
return;
}
window.location.href = `/payment/failed?code=${encodeURIComponent(payload.code)}`;
};
}
```
:::
**Vue 3 完整示例**
以下示例展示了如何在 Vue 3 项目中集成 SDK,使用 Composition API 实现响应式状态管理。
::: code-tree title="Vue 3 集成示例" height="600px" entry="App.vue"
```vue:line-numbers title="App.vue" :active
## iOS
## 接入摘要
Native SDK 适用于商户自有 iOS 或 Android App 内的支付场景。商户服务端调用 [prePay(预下单)接口](/notes/zh/checkout/api/reserve/) 创建支付会话,App 客户端使用响应中的 `token` 唤起 PingPong 半屏收银台,买家无需离开当前 App 即可完成支付流程。
::: tip 适用场景
适合希望在移动 App 内承载标准化收银台、减少支付方式展示和支付交互开发成本,并保留原生 App 体验的商户。支付完成后的订单状态仍应以服务端收到的 [支付通知](/notes/zh/notify/payment/notify/) 或 [交易查询](/notes/zh/checkout/api/getOne/) 为准。
:::
## 支付体验

## 支付流程
```mermaid
sequenceDiagram
participant Buyer as 买家
participant App as 商户 App
participant Server as 商户服务端
participant SDK as PingPong Native SDK
participant PP as PingPongCheckout
participant Channel as 发卡行/支付渠道
Buyer->>App: 1. 确认订单并点击支付
App->>Server: 2. 请求创建支付会话
Server->>PP: 3. 调用 prePay(预下单)接口
PP-->>Server: 4. 返回 token / transactionId
Server-->>App: 5. 返回 token
App->>SDK: 6. 传入 token 并唤起半屏收银台
SDK->>PP: 7. 验证 token 并获取可用支付方式
PP-->>SDK: 8. 返回支付方式列表
SDK-->>Buyer: 9. 展示半屏支付面板
Buyer->>SDK: 10. 选择支付方式并确认支付
SDK->>PP: 11. 提交支付请求
PP->>Channel: 12. 路由至支付渠道处理
Channel-->>PP: 13. 返回处理结果
PP-->>SDK: 14. 返回支付流程状态
SDK-->>App: 15. 触发 completed / failure / cancel 回调
PP-->>Server: 16. 推送支付异步通知
App->>Server: 17. 查询商户订单最终状态
```
::: steps
1. 创建支付会话
商户服务端调用 [prePay(预下单)接口](/notes/zh/checkout/api/reserve/) 创建支付会话,并保存 `merchantTransactionId`、`transactionId` 与订单状态。
2. 返回 SDK token
商户服务端从响应中获取 `token`,再返回给 iOS 或 Android 客户端。`salt`、签名逻辑和完整请求参数必须只保存在服务端。
3. 唤起半屏收银台
App 客户端完成 SDK 初始化后,传入 `token` 唤起 PingPong 半屏收银台。买家在半屏面板内选择支付方式并完成支付。
4. 处理客户端回调
SDK 回调用于驱动 App 页面交互,例如展示支付中、失败提示或用户取消提示。`completed` / `Completed` 只表示 SDK 支付流程已提交,不代表交易最终成功。
5. 确认最终结果
商户服务端通过支付通知或交易查询确认最终交易状态,并以幂等方式更新订单、发货或发放权益。
:::
## 接入准备
开始接入前,请确认以下信息已经准备完成。
| 准备项 | 说明 |
|:---|:---|
| 账号信息 | 已获取 `clientId`、`accId`,并确认店铺状态可正常交易 |
| API 规则 | 已了解 [API 使用基本规则](/notes/zh/onlinePayment/developer/APIUsage/) 和 [API 端点地址](/notes/zh/onlinePayment/developer/endpoint/) |
| 签名能力 | 已根据 [签名规约](/notes/zh/onlinePayment/developer/sign/) 在服务端完成请求签名,`salt` 不可下发到 App 客户端 |
| 通知地址 | 已准备公网可访问的 `notificationUrl`,用于接收支付结果异步通知 |
| 结果确认 | 已在商户服务端支持支付通知验签、金额币种核对、幂等更新和交易查询兜底 |
| 支付方式 | 已开通本次需要展示在 App 收银台中的支付方式,并确认支持的国家、币种、金额范围和退款能力 |
::: warning 注意
`notificationUrl` 必须是公网可访问的完整地址,不能填写 `localhost`、`127.0.0.1` 或内网 IP。客户端回调不能作为订单落账、发货或发放权益的唯一依据。
:::
## 步骤 1:创建支付会话
买家在 App 内确认订单后,商户服务端调用 [prePay(预下单)接口](/notes/zh/checkout/api/reserve/) 创建支付会话。请不要在 iOS 或 Android 客户端直接调用该接口,避免签名密钥泄露。
支付请求关键参数:
| 参数 | 必填 | 说明 |
|:---|:---:|:---|
| `captureDelayHours` | O | 资金请款方式。默认自动请款;如需手动请款,请按支付方式能力和接口规则配置 |
| `amount` | M | 交易金额 |
| `currency` | M | ISO 4217 三位交易币种 |
| `merchantTransactionId` | M | 商户网站订单流水号,订单唯一标识 |
| `shopperIP` | M | 买家下单 IP |
| `merchantUserId` | M | 商户侧用户唯一 ID |
| `goods` | M | 商品信息,至少包含商品名称、单价和数量 |
| `customer` | C | 买家信息。不同支付方式可能要求邮箱、手机号或账单信息,请以目标支付方式要求为准 |
| `billingAddress` | C | 账单地址。实物类或特定行业场景可能必传,请以接口必传规则和支付方式要求为准 |
| `paymentMethods` | O | 指定收银台可展示的支付方式范围,不传或为空时按店铺配置展示 |
| `notificationUrl` | O | 支付结果通知地址,强烈建议传入 |
| `closeNotificationUrl` | O | 关单通知地址。传入后,可接收主动关单或被动关单结果通知 |
| `payResultUrl` | O | 支付完成后的商户结果页地址。Native SDK 场景通常由 App 自行处理页面展示 |
| `language` | O | 收银台展示语言,未传入时默认展示英文,枚举值参考[语言列表](/notes/zh/appendix/languageCode/) |
有关完整参数的更多信息,请参阅 [prePay(预下单)接口](/notes/zh/checkout/api/reserve/)。
支付请求示例(仅展示 `bizContent` 内容):
```json
{
"captureDelayHours": "0",
"amount": "100",
"currency": "USD",
"merchantTransactionId": "{{merchantTransactionId}}",
"payResultUrl": "https://www.example.com/app/payment-result",
"notificationUrl": "https://www.example.com/checkout/notify",
"closeNotificationUrl": "https://www.example.com/checkout/close-notify",
"language": "en",
"shopperIP": "222.126.52.23",
"merchantUserId": "USER_12345",
"customer": {
"email": "buyer@example.com"
},
"paymentMethods": [],
"goods": [
{
"name": "Digital product",
"description": "Order item",
"unitPrice": "100",
"number": "1",
"virtualProduct": "Y",
"imgUrl": "https://www.example.com/product/SKU_001.png"
}
]
}
```
支付响应示例(仅展示 `bizContent` 内容):
```json
{
"amount": "100",
"currency": "USD",
"transactionId": "26070600000751051115",
"merchantTransactionId": "PMT-A1XGH96WNE1783303688835",
"token": "EU:OYUi5wmfOMRNFJnqmPRR2QHvtJihf8cTKvay2yeHTuRbnqIBpMJra9KtN9YQ_sXo8DsnSxGh_e_6tMpzRzlXjA==",
"paymentUrl": "https://sandbox-safepay.pingpongx.com?token=EU:OYUi5wmfOMRNFJnqmPRR2QHvtJihf8cTKvay2yeHTuRbnqIBpMJra9KtN9YQ_sXo8DsnSxGh_e_6tMpzRzlXjA==",
"innerJsUrl": "https://payssr-cdn.pingpongx.com/production-fra/acquirer-checkout-web/sandbox/pp-checkout.js?token=EU:OYUi5wmfOMRNFJnqmPRR2QHvtJihf8cTKvay2yeHTuRbnqIBpMJra9KtN9YQ_sXo8DsnSxGh_e_6tMpzRzlXjA=="
}
```
支付响应关键字段:
| 参数 | 说明 |
|:---|:---|
| `token` | Native SDK 初始化令牌。App 客户端使用该值唤起半屏收银台 |
| `transactionId` | PingPong 交易流水号,用于后续查询、退款、对账 |
| `merchantTransactionId` | 商户网站订单流水号 |
| `amount` | 交易金额 |
| `currency` | 交易币种 |
::: tip Token 传递
商户服务端应只向 App 客户端返回唤起 SDK 所需的 `token`、订单号和必要展示信息。`accId`、`clientId`、`sign`、`salt` 等签名相关信息不应下发到客户端。
:::
## 步骤 2:前端引入 SDK
### 环境要求
| 项目 | 要求 |
|------|------|
| iOS 版本 | 15.6+ |
### 手动集成
请在完成“接入准备”后,再将 iOS SDK 包引入 App 项目。
1. 下载 [iOS SDK 包](https://cdn18.pingpongx.com/static_platform/acquirer/native/iOS/payment.zip),解压后获取 `.framework` 和 `PPCashDeskSDKBundle.bundle` 资源文件。
2. 将以上两个文件拖动到项目路径中。
3. 确认 `.framework` 已加入 App Target 的 `Frameworks, Libraries, and Embedded Content`,并按项目实际情况设置为 `Embed & Sign`。
### 依赖项
Podfile 中需保证以下依赖项:
| 依赖库 | 版本 | 说明 |
|--------|------|------|
| AFNetworking | 4.x | 网络请求 |
| SDWebImage | 5.x | 图片加载与缓存 |
| MJExtension | 3.x | JSON 与模型转换 |
| MJRefresh | 3.x | 下拉刷新与上拉加载 |
| Masonry | 1.x | 自动布局 |
| MBProgressHUD | 1.x | 加载提示 |
| Bugly | - | 错误监控 |
::: tip iOS 端差异说明
上方“支付流程”已覆盖 Native SDK 的完整链路。iOS 端与通用流程的差异主要体现在客户端调用方式:App 完成 SDK 初始化后,通过 `initWithToken` 唤起半屏收银台,并在支付过程中处理 `completed`、`failure`、`cancel` 三类回调。
:::
## 步骤 3:配置并初始化 SDK
### 关键对象说明
| 类名 | 说明 |
|------|------|
| PPCDManager | SDK 主入口类,单例模式 |
| PPCDConfig | SDK 配置类 |
| PPPaymentRequest | 支付请求参数 |
| PPPaymentResult | 支付结果回调 |
### 环境配置
**环境枚举**:
```objc:line-numbers title="PPCDEnvironmentType.h"
typedef NS_ENUM(NSInteger, PPCDEnvironmentType) {
PPCDEnvironmentTypeRelease = 1, // 生产欧洲
PPCDEnvironmentTypeSandBox = 4, // 沙箱
PPCDEnvironmentTypeReleaseUS = 6, // 生产美国
};
```
| 枚举值 | 值 | 说明 | API 端点 |
|--------|-----|------|----------|
| PPCDEnvironmentTypeSandBox | 4 | 沙箱环境 | `https://sandbox-acquirer-payment.pingpongx.com` |
| PPCDEnvironmentTypeRelease | 1 | 生产环境-欧洲 | `https://acquirer-payment.pingpongx.com` |
| PPCDEnvironmentTypeReleaseUS | 6 | 生产环境-美国 | `https://acquirer-payment-checkout-us.pingpongx.com` |
**配置示例**:
```objc:line-numbers title="PPCDConfig 初始化"
PPCDConfig *config = [[PPCDConfig alloc] init];
config.environmentType = PPCDEnvironmentTypeSandBox; // 沙箱
// config.environmentType = PPCDEnvironmentTypeRelease; // 生产欧洲
// config.environmentType = PPCDEnvironmentTypeReleaseUS; // 生产美国
```
### 初始化 SDK
```objc:line-numbers title="AppDelegate.m"
#import
// 获取 SDK 实例
PPCDManager *manager = [PPCDManager sharedInstance];
// 配置 SDK
PPCDConfig *config = [[PPCDConfig alloc] init];
config.environmentType = PPCDEnvironmentTypeRelease; // 设置网络环境
config.shouldStartRecLog = YES; // 开启日志记录
config.cardBinLengthValue = 11; // 设置卡Bin位数长度
manager.config = config;
```
**配置参数说明**:
| 参数 | 类型 | 说明 |
|------|------|------|
| environmentType | enum | PPCDEnvironmentTypeSandBox / PPCDEnvironmentTypeRelease |
| shouldStartRecLog | BOOL | 是否开启日志记录 |
| cardBinLengthValue | int | 设置卡Bin位数长度 |
## 步骤 4:唤起半屏支付
```objc:line-numbers title="PaymentViewController.m"
[manager initWithToken:@"your_token"
completed:^(NSString *code) {
// 支付流程已提交,需继续向商户服务端确认最终状态
NSLog(@"支付流程完成: %@", code);
}
failure:^(NSError *error) {
// 支付失败回调
NSLog(@"支付流程失败: %@", error.localizedDescription);
}
cancel:^{
// 支付取消回调
NSLog(@"用户取消支付");
}];
```
::: warning 回调说明
`completed` 回调不等同于支付成功。App 收到该回调后,应调用商户服务端查询订单状态,或等待服务端通过支付通知完成订单状态更新。
:::
## 步骤 5:确认最终支付结果
Native SDK 的客户端回调只用于说明当前 App 内支付流程的交互状态,不应直接作为订单成功依据。商户服务端应通过 [支付通知](/notes/zh/notify/payment/notify/) 或 [交易查询](/notes/zh/checkout/api/getOne/) 获取最终交易状态。
| 确认方式 | 说明 | 处理建议 |
|:---|:---|:---|
| 支付通知 | PingPongCheckout 主动向 `notificationUrl` 推送交易结果 | 推荐优先接入。收到通知后先验签、核对金额币种,再按 `status` 幂等更新订单 |
| 交易查询 | 商户服务端主动调用交易查询接口获取交易状态 | 用于通知延迟、未达、回调处理异常,或 App 需要立即刷新订单状态的场景 |
::: warning 重要说明
客户端收到 iOS `completed` 或 Android `Completed` 回调时,建议在 App 内展示“支付处理中”或向商户服务端查询订单状态。订单落账、发货和发放权益必须以服务端确认的最终状态为准。
:::
### SDK 回调处理建议
| 场景 | iOS 回调 | Android 回调 | App 侧处理建议 |
|:---|:---|:---|:---|
| 支付流程已提交 | `completed` | `Completed` | 展示支付处理中,并调用商户服务端查询订单状态 |
| 用户关闭半屏收银台 | `cancel` | `Canceled` | 返回订单页,可提示用户支付已取消或允许重新发起支付 |
| SDK 调用异常或支付流程失败 | `failure` | `Failed` | 展示失败原因或通用重试提示,必要时允许重新创建支付会话 |
### 支付通知处理建议
如果通过异步通知获取结果,商户服务端应优先返回 HTTP `2xx` 确认已接收,再根据通知中的 `status` 更新订单状态。
支付通知示例:
```json
{
"clientId": "2023121216311410287",
"code": "000000",
"bizContent": "{\"exchangedCurrency\":\"USD\",\"amount\":\"100.000000\",\"transactionTime\":\"1783303837000\",\"transactionId\":\"26070600000751051115\",\"notifyType\":\"RECHARGE\",\"transactionEndTime\":\"1783303856604\",\"merchantTransactionId\":\"PMT-A1XGH96WNE1783303688835\",\"paymentMethod\":{\"type\":\"ApplePay\"},\"currency\":\"USD\",\"exchangedAmount\":\"100.000000\",\"captureDelayHours\":0,\"status\":\"SUCCESS\"}",
"sign": "A9383DCF4FF42C07423C2BDD77490622AA08DF9155D125683BA8BB794C24CCEC",
"accId": "2023121216311410287522",
"description": "Transaction succeeded",
"signType": "SHA256"
}
```
通知关键字段:
| 参数 | 说明 | 处理建议 |
|:---|:---|:---|
| `transactionId` | PingPong 交易流水号 | 保存并用于查询、退款、对账 |
| `merchantTransactionId` | 商户网站订单流水号 | 用于匹配商户侧订单 |
| `status` | 交易状态 | 按状态处理订单,不要仅依赖通知接口返回 `code` |
| `amount` | 交易金额 | 与商户订单金额核对 |
| `currency` | 交易币种 | 与商户订单币种核对 |
| `paymentMethod.type` | 买家实际使用的支付方式 | 可用于订单展示、对账或支付方式统计 |
| `notifyType` | 通知类型,支付通知固定为 `RECHARGE` | 用于区分通知事件类型 |
通知中的 `status` 建议按以下方式处理:
| status | 含义 | 商户处理建议 |
|:---|:---|:---|
| `SUCCESS` | 支付成功 | 将订单更新为支付成功,继续履约、发货或发放权益 |
| `FAILED` | 支付失败 | 将订单更新为支付失败,可引导买家重新支付 |
| `CANCEL` | 风控审核拒绝 | 将订单更新为失败或关闭状态,不应继续履约 |
| `AUTH_SUCCESS` | 预授权成功 | 将订单更新为已授权,后续按业务需要发起 Capture 或 Void |
| `CLOSED` | 订单关闭 | 仅在上送 `closeNotificationUrl` 的情况下,才会发送主动关单或被动关单通知 |
收到通知后,您无需对响应内容进行加签处理。但无论订单支付是否成功,均必须严格按照以下固定格式返回响应。
通知响应示例:
```json
{
"status": "SUCCESS"
}
```
### 交易查询处理建议
当异步通知延迟、未达、回调处理失败,或 App 需要在买家完成支付后立即刷新订单状态时,可以调用 [交易查询](/notes/zh/checkout/api/getOne/) 获取交易最新状态。
交易查询请求示例(仅展示 `bizContent` 内容):
```json
{
"merchantTransactionId": "PMT-A1XGH96WNE1783303688835"
}
```
交易查询响应示例(仅展示 `bizContent` 内容):
```json
{
"resultCode": "000000",
"transactionTime": "1783303690000",
"merchantTransactionId": "PMT-A1XGH96WNE1783303688835",
"currency": "USD",
"exchangedAmount": "100.000000",
"resultDescription": "Transaction succeeded",
"exchangedCurrency": "USD",
"amount": "100.000000",
"transactionEndingTime": "1783303857000",
"transactionId": "26070600000751051115",
"paymentMethod": {
"type": "ApplePay"
},
"captureDelayHours": 0,
"status": "SUCCESS"
}
```
查询响应关键字段:
| 参数 | 说明 | 处理建议 |
|:---|:---|:---|
| `transactionId` | PingPong 交易流水号 | 保存并用于退款、对账或后续查询 |
| `merchantTransactionId` | 商户网站订单流水号 | 用于匹配商户侧订单 |
| `status` | 交易状态 | 商户订单状态应以该字段为准 |
| `amount` | 交易金额 | 与商户订单金额核对 |
| `currency` | 交易币种 | 与商户订单币种核对 |
| `paymentMethod.type` | 买家实际使用的支付方式 | 可用于订单展示、对账或支付方式统计 |
| `transactionEndingTime` | 交易结束时间 | 可用于订单完成时间记录 |
查询状态处理建议:
| status | 含义 | 商户处理建议 |
|:---|:---|:---|
| `INIT` | 初始态 | 不更新为成功,继续等待通知或稍后查询 |
| `PROCESSING` | 进行中 | 不更新为成功,继续等待通知或稍后查询 |
| `SUCCESS` | 成功 | 更新订单为支付成功,继续履约、发货或发放权益 |
| `FAILED` | 失败 | 更新订单为支付失败,可引导买家重新支付 |
| `AUTH_SUCCESS` | 预授权成功 | 更新订单为已授权,后续按业务需要发起 Capture 或 Void |
| `CANCEL` | 预授权取消/人工审核拒绝 | 更新订单为失败或关闭状态,不应继续履约 |
## 上线检查
上线前建议完成以下检查:
1. 服务端已完成请求签名、响应验签、通知验签和幂等处理。
2. App 客户端不会保存或暴露 `salt`、`sign` 等服务端签名信息。
3. `notificationUrl` 已在公网可访问,并能在收到通知后返回固定响应。
4. 支付成功、失败、取消、处理中、预授权成功等状态均已完成订单状态映射。
5. 已分别在沙箱环境验证银行卡、Apple Pay 或 Google Pay 等目标支付方式。
6. 生产环境上线前,已切换到对应区域的 API 端点和 SDK 环境枚举。
## Apple Pay 配置
如需在 iOS Native SDK 中展示 Apple Pay,请先确认商户号和 App 已完成 Apple Pay 相关配置。更多支付方式能力说明可参考 [Apple Pay](/notes/zh/paymentMethods/ApplePay/)。
### 配置开发者账户
1. **创建商家标识符**:
- 登录 Apple 开发者中心,选择 "Merchant IDs"
- 输入唯一标识符(格式:`merchant.com.{app_name}`)
2. **生成付款处理证书**:
- 在开发者中心选择对应商家标识符,点击 "Create Certificate"
- 下载 CSR 文件(通过 Xcode 或终端生成),上传后获取 `.cer` 证书文件
3. **传入 Merchant ID**:
```objc:line-numbers title="ApplePay Merchant ID 配置"
PPCDConfig *config = [[PPCDConfig alloc] init];
config.applePayMerchantId = @"merchant.com.yourapp";
manager.config = config;
```
## Android
## 接入摘要
Native SDK 适用于商户自有 iOS 或 Android App 内的支付场景。商户服务端调用 [prePay(预下单)接口](/notes/zh/checkout/api/reserve/) 创建支付会话,App 客户端使用响应中的 `token` 唤起 PingPong 半屏收银台,买家无需离开当前 App 即可完成支付流程。
::: tip 适用场景
适合希望在移动 App 内承载标准化收银台、减少支付方式展示和支付交互开发成本,并保留原生 App 体验的商户。支付完成后的订单状态仍应以服务端收到的 [支付通知](/notes/zh/notify/payment/notify/) 或 [交易查询](/notes/zh/checkout/api/getOne/) 为准。
:::
## 支付体验

## 支付流程
```mermaid
sequenceDiagram
participant Buyer as 买家
participant App as 商户 App
participant Server as 商户服务端
participant SDK as PingPong Native SDK
participant PP as PingPongCheckout
participant Channel as 发卡行/支付渠道
Buyer->>App: 1. 确认订单并点击支付
App->>Server: 2. 请求创建支付会话
Server->>PP: 3. 调用 prePay(预下单)接口
PP-->>Server: 4. 返回 token / transactionId
Server-->>App: 5. 返回 token
App->>SDK: 6. 传入 token 并唤起半屏收银台
SDK->>PP: 7. 验证 token 并获取可用支付方式
PP-->>SDK: 8. 返回支付方式列表
SDK-->>Buyer: 9. 展示半屏支付面板
Buyer->>SDK: 10. 选择支付方式并确认支付
SDK->>PP: 11. 提交支付请求
PP->>Channel: 12. 路由至支付渠道处理
Channel-->>PP: 13. 返回处理结果
PP-->>SDK: 14. 返回支付流程状态
SDK-->>App: 15. 触发 completed / failure / cancel 回调
PP-->>Server: 16. 推送支付异步通知
App->>Server: 17. 查询商户订单最终状态
```
::: steps
1. 创建支付会话
商户服务端调用 [prePay(预下单)接口](/notes/zh/checkout/api/reserve/) 创建支付会话,并保存 `merchantTransactionId`、`transactionId` 与订单状态。
2. 返回 SDK token
商户服务端从响应中获取 `token`,再返回给 iOS 或 Android 客户端。`salt`、签名逻辑和完整请求参数必须只保存在服务端。
3. 唤起半屏收银台
App 客户端完成 SDK 初始化后,传入 `token` 唤起 PingPong 半屏收银台。买家在半屏面板内选择支付方式并完成支付。
4. 处理客户端回调
SDK 回调用于驱动 App 页面交互,例如展示支付中、失败提示或用户取消提示。`completed` / `Completed` 只表示 SDK 支付流程已提交,不代表交易最终成功。
5. 确认最终结果
商户服务端通过支付通知或交易查询确认最终交易状态,并以幂等方式更新订单、发货或发放权益。
:::
## 接入准备
开始接入前,请确认以下信息已经准备完成。
| 准备项 | 说明 |
|:---|:---|
| 账号信息 | 已获取 `clientId`、`accId`,并确认店铺状态可正常交易 |
| API 规则 | 已了解 [API 使用基本规则](/notes/zh/onlinePayment/developer/APIUsage/) 和 [API 端点地址](/notes/zh/onlinePayment/developer/endpoint/) |
| 签名能力 | 已根据 [签名规约](/notes/zh/onlinePayment/developer/sign/) 在服务端完成请求签名,`salt` 不可下发到 App 客户端 |
| 通知地址 | 已准备公网可访问的 `notificationUrl`,用于接收支付结果异步通知 |
| 结果确认 | 已在商户服务端支持支付通知验签、金额币种核对、幂等更新和交易查询兜底 |
| 支付方式 | 已开通本次需要展示在 App 收银台中的支付方式,并确认支持的国家、币种、金额范围和退款能力 |
::: warning 注意
`notificationUrl` 必须是公网可访问的完整地址,不能填写 `localhost`、`127.0.0.1` 或内网 IP。客户端回调不能作为订单落账、发货或发放权益的唯一依据。
:::
## 步骤 1:创建支付会话
买家在 App 内确认订单后,商户服务端调用 [prePay(预下单)接口](/notes/zh/checkout/api/reserve/) 创建支付会话。请不要在 iOS 或 Android 客户端直接调用该接口,避免签名密钥泄露。
支付请求关键参数:
| 参数 | 必填 | 说明 |
|:---|:---:|:---|
| `captureDelayHours` | O | 资金请款方式。默认自动请款;如需手动请款,请按支付方式能力和接口规则配置 |
| `amount` | M | 交易金额 |
| `currency` | M | ISO 4217 三位交易币种 |
| `merchantTransactionId` | M | 商户网站订单流水号,订单唯一标识 |
| `shopperIP` | M | 买家下单 IP |
| `merchantUserId` | M | 商户侧用户唯一 ID |
| `goods` | M | 商品信息,至少包含商品名称、单价和数量 |
| `customer` | C | 买家信息。不同支付方式可能要求邮箱、手机号或账单信息,请以目标支付方式要求为准 |
| `billingAddress` | C | 账单地址。实物类或特定行业场景可能必传,请以接口必传规则和支付方式要求为准 |
| `paymentMethods` | O | 指定收银台可展示的支付方式范围,不传或为空时按店铺配置展示 |
| `notificationUrl` | O | 支付结果通知地址,强烈建议传入 |
| `closeNotificationUrl` | O | 关单通知地址。传入后,可接收主动关单或被动关单结果通知 |
| `payResultUrl` | O | 支付完成后的商户结果页地址。Native SDK 场景通常由 App 自行处理页面展示 |
| `language` | O | 收银台展示语言,未传入时默认展示英文,枚举值参考[语言列表](/notes/zh/appendix/languageCode/) |
有关完整参数的更多信息,请参阅 [prePay(预下单)接口](/notes/zh/checkout/api/reserve/)。
支付请求示例(仅展示 `bizContent` 内容):
```json
{
"captureDelayHours": "0",
"amount": "100",
"currency": "USD",
"merchantTransactionId": "{{merchantTransactionId}}",
"payResultUrl": "https://www.example.com/app/payment-result",
"notificationUrl": "https://www.example.com/checkout/notify",
"closeNotificationUrl": "https://www.example.com/checkout/close-notify",
"language": "en",
"shopperIP": "222.126.52.23",
"merchantUserId": "USER_12345",
"customer": {
"email": "buyer@example.com"
},
"paymentMethods": [],
"goods": [
{
"name": "Digital product",
"description": "Order item",
"unitPrice": "100",
"number": "1",
"virtualProduct": "Y",
"imgUrl": "https://www.example.com/product/SKU_001.png"
}
]
}
```
支付响应示例(仅展示 `bizContent` 内容):
```json
{
"amount": "100",
"currency": "USD",
"transactionId": "26070600000751051115",
"merchantTransactionId": "PMT-A1XGH96WNE1783303688835",
"token": "EU:OYUi5wmfOMRNFJnqmPRR2QHvtJihf8cTKvay2yeHTuRbnqIBpMJra9KtN9YQ_sXo8DsnSxGh_e_6tMpzRzlXjA==",
"paymentUrl": "https://sandbox-safepay.pingpongx.com?token=EU:OYUi5wmfOMRNFJnqmPRR2QHvtJihf8cTKvay2yeHTuRbnqIBpMJra9KtN9YQ_sXo8DsnSxGh_e_6tMpzRzlXjA==",
"innerJsUrl": "https://payssr-cdn.pingpongx.com/production-fra/acquirer-checkout-web/sandbox/pp-checkout.js?token=EU:OYUi5wmfOMRNFJnqmPRR2QHvtJihf8cTKvay2yeHTuRbnqIBpMJra9KtN9YQ_sXo8DsnSxGh_e_6tMpzRzlXjA=="
}
```
支付响应关键字段:
| 参数 | 说明 |
|:---|:---|
| `token` | Native SDK 初始化令牌。App 客户端使用该值唤起半屏收银台 |
| `transactionId` | PingPong 交易流水号,用于后续查询、退款、对账 |
| `merchantTransactionId` | 商户网站订单流水号 |
| `amount` | 交易金额 |
| `currency` | 交易币种 |
::: tip Token 传递
商户服务端应只向 App 客户端返回唤起 SDK 所需的 `token`、订单号和必要展示信息。`accId`、`clientId`、`sign`、`salt` 等签名相关信息不应下发到客户端。
:::
## 步骤 2:前端引入 SDK
### 环境要求
| 项目 | 要求 |
|------|------|
| Android Gradle Plugin(AGP) | 8.13.2 |
| Java | 17 |
| Android SDK | compileSdk 36,targetSdk 36,minSdk 24 |
| OkHttp | 4.12.0 |
| Gson | 2.11.0 |
| Retrofit | 2.11.0 |
### 手动集成
请在完成“接入准备”后,再将 Android SDK 包引入 App 项目。
1. 下载 [Android SDK 包](https://cdn18.pingpongx.com/static_platform/acquirer/native/Android/payment.aar.zip),解压后获取 aar 文件。
2. Android Studio 工程中引入依赖。
将 aar 文件复制到 module 的 libs 目录下,在 module 的 gradle 文件中添加依赖:
```groovy:line-numbers title="build.gradle"
dependencies {
implementation files('libs/payment-android-sdk-1.0.0.aar')
}
```
::: tip Android 端差异说明
上方“支付流程”已覆盖 Native SDK 的完整链路。Android 端与通用流程的差异主要体现在客户端调用方式:App 侧先创建 `PPPayment` 实例,再通过 `presentPayment` 唤起半屏收银台,并在支付过程中处理 `Completed`、`Failed`、`Canceled` 三类回调。
:::
## 步骤 3:配置并初始化 SDK
### 关键对象说明
| 类名 | 说明 |
|------|------|
| PPPayment | SDK 主入口类 |
| PaymentConfig | SDK 配置类 |
| PaymentResult | 支付结果回调(Sealed Class) |
| Environment | 环境枚举(SANDBOX/ONLINE/ONLINE_US) |
### 环境配置
**环境枚举**:
```kotlin:line-numbers title="Environment.kt"
enum class Environment {
SANDBOX, // 沙箱环境
ONLINE, // 生产环境-欧洲
ONLINE_US // 生产环境-美国
}
```
| 枚举值 | 说明 | API 端点 |
|--------|------|----------|
| SANDBOX | 沙箱环境 | `https://sandbox-acquirer-payment.pingpongx.com` |
| ONLINE | 生产环境-欧洲 | `https://acquirer-payment.pingpongx.com` |
| ONLINE_US | 生产环境-美国 | `https://acquirer-payment-checkout-us.pingpongx.com` |
**跨平台映射**:
| Android | iOS 等价 | 说明 |
|---------|---------|------|
| `SANDBOX` | `PPCDEnvironmentTypeSandBox` | 沙箱环境 |
| `ONLINE` | `PPCDEnvironmentTypeRelease` | 生产欧洲 |
| `ONLINE_US` | `PPCDEnvironmentTypeReleaseUS` | 生产美国 |
**配置示例**:
```kotlin:line-numbers title="PaymentConfig 初始化"
val config = PaymentConfig(
environment = Environment.SANDBOX, // 沙箱
// environment = Environment.ONLINE, // 生产欧洲
// environment = Environment.ONLINE_US // 生产美国
logEnabled = true,
cardBinLength = true
)
```
### 初始化 SDK
```kotlin:line-numbers title="PaymentConfig.kt"
val config = PaymentConfig(
environment = Environment.SANDBOX, // 控制环境切换
logEnabled = true, // 控制 SDK 日志开关
cardBinLength = true // 设置卡 Bin 位数长度
)
```
## 步骤 4:唤起半屏支付
```kotlin:line-numbers title="PaymentActivity.kt"
// 1. 创建支付实例
val payment = PPPayment(activity, PaymentResultCallback { result ->
when (result) {
is PaymentResult.Completed -> {
// 支付流程已提交,需继续向商户服务端确认最终状态
}
is PaymentResult.Canceled -> {
// 用户取消
}
is PaymentResult.Failed -> {
// 支付失败
}
}
})
// 2. 唤起收银台
payment.presentPayment(
token = "your_token",
config = config
)
```
::: warning 回调说明
`Completed` 回调不等同于支付成功。App 收到该回调后,应调用商户服务端查询订单状态,或等待服务端通过支付通知完成订单状态更新。
:::
## 步骤 5:确认最终支付结果
Native SDK 的客户端回调只用于说明当前 App 内支付流程的交互状态,不应直接作为订单成功依据。商户服务端应通过 [支付通知](/notes/zh/notify/payment/notify/) 或 [交易查询](/notes/zh/checkout/api/getOne/) 获取最终交易状态。
| 确认方式 | 说明 | 处理建议 |
|:---|:---|:---|
| 支付通知 | PingPongCheckout 主动向 `notificationUrl` 推送交易结果 | 推荐优先接入。收到通知后先验签、核对金额币种,再按 `status` 幂等更新订单 |
| 交易查询 | 商户服务端主动调用交易查询接口获取交易状态 | 用于通知延迟、未达、回调处理异常,或 App 需要立即刷新订单状态的场景 |
::: warning 重要说明
客户端收到 iOS `completed` 或 Android `Completed` 回调时,建议在 App 内展示“支付处理中”或向商户服务端查询订单状态。订单落账、发货和发放权益必须以服务端确认的最终状态为准。
:::
### SDK 回调处理建议
| 场景 | iOS 回调 | Android 回调 | App 侧处理建议 |
|:---|:---|:---|:---|
| 支付流程已提交 | `completed` | `Completed` | 展示支付处理中,并调用商户服务端查询订单状态 |
| 用户关闭半屏收银台 | `cancel` | `Canceled` | 返回订单页,可提示用户支付已取消或允许重新发起支付 |
| SDK 调用异常或支付流程失败 | `failure` | `Failed` | 展示失败原因或通用重试提示,必要时允许重新创建支付会话 |
### 支付通知处理建议
如果通过异步通知获取结果,商户服务端应优先返回 HTTP `2xx` 确认已接收,再根据通知中的 `status` 更新订单状态。
支付通知示例:
```json
{
"clientId": "2023121216311410287",
"code": "000000",
"bizContent": "{\"exchangedCurrency\":\"USD\",\"amount\":\"100.000000\",\"transactionTime\":\"1783303837000\",\"transactionId\":\"26070600000751051115\",\"notifyType\":\"RECHARGE\",\"transactionEndTime\":\"1783303856604\",\"merchantTransactionId\":\"PMT-A1XGH96WNE1783303688835\",\"paymentMethod\":{\"type\":\"ApplePay\"},\"currency\":\"USD\",\"exchangedAmount\":\"100.000000\",\"captureDelayHours\":0,\"status\":\"SUCCESS\"}",
"sign": "A9383DCF4FF42C07423C2BDD77490622AA08DF9155D125683BA8BB794C24CCEC",
"accId": "2023121216311410287522",
"description": "Transaction succeeded",
"signType": "SHA256"
}
```
通知关键字段:
| 参数 | 说明 | 处理建议 |
|:---|:---|:---|
| `transactionId` | PingPong 交易流水号 | 保存并用于查询、退款、对账 |
| `merchantTransactionId` | 商户网站订单流水号 | 用于匹配商户侧订单 |
| `status` | 交易状态 | 按状态处理订单,不要仅依赖通知接口返回 `code` |
| `amount` | 交易金额 | 与商户订单金额核对 |
| `currency` | 交易币种 | 与商户订单币种核对 |
| `paymentMethod.type` | 买家实际使用的支付方式 | 可用于订单展示、对账或支付方式统计 |
| `notifyType` | 通知类型,支付通知固定为 `RECHARGE` | 用于区分通知事件类型 |
通知中的 `status` 建议按以下方式处理:
| status | 含义 | 商户处理建议 |
|:---|:---|:---|
| `SUCCESS` | 支付成功 | 将订单更新为支付成功,继续履约、发货或发放权益 |
| `FAILED` | 支付失败 | 将订单更新为支付失败,可引导买家重新支付 |
| `CANCEL` | 风控审核拒绝 | 将订单更新为失败或关闭状态,不应继续履约 |
| `AUTH_SUCCESS` | 预授权成功 | 将订单更新为已授权,后续按业务需要发起 Capture 或 Void |
| `CLOSED` | 订单关闭 | 仅在上送 `closeNotificationUrl` 的情况下,才会发送主动关单或被动关单通知 |
收到通知后,您无需对响应内容进行加签处理。但无论订单支付是否成功,均必须严格按照以下固定格式返回响应。
通知响应示例:
```json
{
"status": "SUCCESS"
}
```
### 交易查询处理建议
当异步通知延迟、未达、回调处理失败,或 App 需要在买家完成支付后立即刷新订单状态时,可以调用 [交易查询](/notes/zh/checkout/api/getOne/) 获取交易最新状态。
交易查询请求示例(仅展示 `bizContent` 内容):
```json
{
"merchantTransactionId": "PMT-A1XGH96WNE1783303688835"
}
```
交易查询响应示例(仅展示 `bizContent` 内容):
```json
{
"resultCode": "000000",
"transactionTime": "1783303690000",
"merchantTransactionId": "PMT-A1XGH96WNE1783303688835",
"currency": "USD",
"exchangedAmount": "100.000000",
"resultDescription": "Transaction succeeded",
"exchangedCurrency": "USD",
"amount": "100.000000",
"transactionEndingTime": "1783303857000",
"transactionId": "26070600000751051115",
"paymentMethod": {
"type": "ApplePay"
},
"captureDelayHours": 0,
"status": "SUCCESS"
}
```
查询响应关键字段:
| 参数 | 说明 | 处理建议 |
|:---|:---|:---|
| `transactionId` | PingPong 交易流水号 | 保存并用于退款、对账或后续查询 |
| `merchantTransactionId` | 商户网站订单流水号 | 用于匹配商户侧订单 |
| `status` | 交易状态 | 商户订单状态应以该字段为准 |
| `amount` | 交易金额 | 与商户订单金额核对 |
| `currency` | 交易币种 | 与商户订单币种核对 |
| `paymentMethod.type` | 买家实际使用的支付方式 | 可用于订单展示、对账或支付方式统计 |
| `transactionEndingTime` | 交易结束时间 | 可用于订单完成时间记录 |
查询状态处理建议:
| status | 含义 | 商户处理建议 |
|:---|:---|:---|
| `INIT` | 初始态 | 不更新为成功,继续等待通知或稍后查询 |
| `PROCESSING` | 进行中 | 不更新为成功,继续等待通知或稍后查询 |
| `SUCCESS` | 成功 | 更新订单为支付成功,继续履约、发货或发放权益 |
| `FAILED` | 失败 | 更新订单为支付失败,可引导买家重新支付 |
| `AUTH_SUCCESS` | 预授权成功 | 更新订单为已授权,后续按业务需要发起 Capture 或 Void |
| `CANCEL` | 预授权取消/人工审核拒绝 | 更新订单为失败或关闭状态,不应继续履约 |
## 上线检查
上线前建议完成以下检查:
1. 服务端已完成请求签名、响应验签、通知验签和幂等处理。
2. App 客户端不会保存或暴露 `salt`、`sign` 等服务端签名信息。
3. `notificationUrl` 已在公网可访问,并能在收到通知后返回固定响应。
4. 支付成功、失败、取消、处理中、预授权成功等状态均已完成订单状态映射。
5. 已分别在沙箱环境验证银行卡、Apple Pay 或 Google Pay 等目标支付方式。
6. 生产环境上线前,已切换到对应区域的 API 端点和 SDK 环境枚举。
## Google Pay 配置
如需在 Android Native SDK 中展示 Google Pay,请先确认店铺已开通 Google Pay,App 运行环境满足 Google Pay 要求,并按业务所属地区完成必要配置。更多支付方式能力说明可参考 [Google Pay](/notes/zh/paymentMethods/GooglePay/)。
## 支付后操作
以下支付后操作适用于 Web/WAP、iOS 和 Android 集成形态,相关操作均由商户服务端完成。前端结果页或 SDK 回调只用于买家展示与交互反馈,不应直接作为订单最终成功依据;订单状态仍应以 [支付通知](/notes/zh/notify/payment/notify/) 或 [交易查询](/notes/zh/checkout/api/getOne/) 的服务端确认结果为准。
### 查询交易
当买家从结果页返回、App 收到支付完成回调、异步通知延迟或未达,或商户需要执行补偿任务时,可以调用 [交易查询](/notes/zh/checkout/api/getOne/) 获取交易最新状态。
查询时建议优先使用商户侧保存的 `merchantTransactionId` 或 PingPong 返回的 `transactionId` 定位交易;查询请求成功仅表示查询接口处理成功,不代表支付成功,商户订单状态仍应以查询响应中的 `status` 为准。
### 退款
支付成功后,如买家申请退款、商户取消已支付订单或需要退回部分或全部款项,可调用 [申请退款](/notes/zh/checkout/api/modifications/refund/) 对原交易发起退款。
退款结果可通过 [退款通知](/notes/zh/notify/refundNotify/) 接收;如通知延迟、未达或需要再次确认,可调用 [退款查询](/notes/zh/checkout/api/getRefund/) 主动查询。
不同支付方式的退款能力不同,接入前请确认目标支付方式是否支持退款、退款有效期、是否支持部分退款以及是否支持多次部分退款。更完整的退款接入说明可参考 [Refund](/notes/zh/integrate/modify/Refund/)。
### 对账
如需了解账单生成规则与结算周期,可参考 [结算周期和对账单](/notes/zh/reconciliation/settlementCycle/);如需开通账单下载能力,可参阅 [SFTP服务申请](/notes/zh/reconciliation/transactionStatementDownload/)。