商户接入指南 商户接入指南
  • V2
  • V3 (opens new window)
  • V2
  • V3 (opens new window)
  • 接入指南

    • 起步
    • 名词约定
    • 对接流程(必读)
    • 签名规约
    • 服务端接入流程
    • 3D集成指南V1(From提交)
    • 3D集成指南V2(风控插件)
  • 接入方案

    • 收银台模式

      • 内嵌JS-SDK
        • 收银台模式系统交互流程
        • 如何接入
          • 请求地址
          • 请求参数
          • 请求参数签名
          • 加签参数列表
          • 响应参数
        • 唤起收银台
          • 引入JS-SDK
          • 初始化并SDK并渲染收银台
          • 完成付款
        • 3DS
        • 预授权
          • 什么是预授权
          • 如何发起预授权交易
        • CAPTURE
          • 什么是CAPTURE
          • 如何发起
        • VOID
          • 什么是VOID
          • 如何发起
      • 跳转接入
    • 端到端模式

      • 快速开始
      • 商户收银台规范
    • 交易处理方案

      • 处理交易状态
      • 交易数据处理方案
      • 交易状态处理方案
      • 自动续费场景处理方案
    • 开源建站工具插件支持

      • Magento235
      • Woocommerce
      • OpenCart
      • Prestashop
      • ZenCart
  • APIs

    • 交易下单

      • 收银台模式
      • 端到端模式
      • 退款和预授权
      • 修改交易信息
    • 交易查询

    • 快捷支付

    • 拒付查询

    • 物流信息

    • 交易账单

    • 币种汇率

    • 商户信息

  • 附录

  • v2

内嵌SDK

# 快速开始

Tips :

推荐使用内嵌接入

由 pingpong checkout 提供收银台⻚面,商户无需自行开发收银台⻚面,执行完结账请求,直接跳转至 pingpong checkout 收银台⻚面。


# 收银台模式系统交互流程

# 如何接入

  • 调用收银台 v2/checkout 接口

  • v2/checkout 接口返回token

  • 商户将用户⻚面重定向至收银台展示页面,传入token并初始化SDK

  • 用户浏览器渲染PingPongPay 收银台⻚面,完成支付


注意 : 在交易进行中,为了更好地帮助商户提前规避风险,若PingPongPay 风控系统识别出订单风险存疑,会在客户端展示交易状态为“status=REVIEW”,商户收到“status=REVIEW”时,需要及时进行内部审核。

商户可执行2种操作:
  • 审核通过
  • 审核拒绝

若商户7天内无反馈,pingpong checkout 7天后将自动撤销该笔交易。


# 请求地址



https://{host}/v2/checkout
1

# 请求参数


参数必填属性说明:必填(M),可选(O),条件必填(C)。

# 请求参数签名


注意 : 当前接口为部分加签,仅在下面列表中的参数参与签名

# 加签参数列表

参数名 描述
clientId PingPong商户号
accId PingPong商户店铺号
amount 交易金额
currency 交易币种
cardNum 交易卡号
transactionId PingPong交易流水号
merchantTransactionId 商户交易流水号
signType 加签类型
shopperResultUrl 商户自定义接收重定向的结果 URL

# 获取签名参数列表

  1. 准备一个ArrayList 加签参数列表

  2. 遍历收到的请求参数列表。

  3. 每次循环都比较请求参数的参数名是否在加签参数列表

  4. 在加签参数列表的参数加入新的签名参数列表

# PHP伪代码示例:


<?php

$request = isPingPongPay($requestParams);

if(empty($request)){
    throw new Error("验签失败");
}

$needSign = [];

$scope = [
    "clientId",
    "accId",
    "amount",
    "currency",
    "cardNum",
    "transactionId",
    "merchantTransactionId",
    "requestId",
    "signType",
]

foreach($request as $key=>$value){
    if(in_array($key,$scope)){
      $needSign[$key] = $value;   
    }    
 // then if not empty $needSign  you will to Generate signature string  
 
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

# 签名串的生成

  1. 待签名参数列表按照字典序排序

  2. 将排序后的待签名参数列表转成url查询字符串(用'=' 进行参数名和参数值(trim 后的值)的拼接,用'&'进行多 个参数之间的拼接,即 key1=val1&key2=val2&key3=val。)

3.拼接签名密钥(secret): 签名密钥放入签名串的位置为签名串的开头,即{salt}key1=val2&key2=val2&key3=val3

  1. 签名串进行MD5运算后所有字符串转成大写,即可获得正确的签名

# 重要入参说明


注意 :

1. merchantTransactionId 应是全局唯一,不可重复

2. amount 精确到两位小数,没有小数部分的补零。(如 25.12 或 20.00)

3. paymentType 一般为SALE 如果要对接AUTH请在对接过程告知,并在测试报告中声明,一定通过验收后才能上线,以免发生交易问题

4. 在收银台模式下 device browserInfo 可以自动抓取,可以不传

5. shipping billing goods 不能为空 eCommerce airline reCharge carRental 根据行业情况填写,这些参数影响交易成功率,请详实填写。

6. shipping billing state 美国加拿大地区必须填写二字简码,有些地区没有state,可以填写空字符串。

7. merchantUserId 必须全局唯一 没登录或者获取不到的情况请传空字符串。



# 请求示例



    POST /v2/checkout HTTP/1.1
    Host: {host}
    Content-Type: application/json
    Content-Length: 3012
    
    {
        "sign": "3F9DF2F986EFCE55919C2CA991689D4C",
        "signType": "MD5",
        "accId": "2018092714313010016291",
        "amount": "20",
        "currency": "USD",
        "merchantTransactionId": "MTN193495030728",
        "paymentType": "SALE",
        "shopperResultUrl": "http://127.0.0.1:8010/demo/checkoutResult",
        "threeDSecure": "N",
        "riskInfo": {
            "device": {
                "orderTerminal": "01",
                "fingerprintId": "e10adc3949ba59abbe56e057f20f883e"
            },
            "customer": {
                "customerId": "UN00000001",
                "firstName": "James",
                "lastName": "LeBron",
                "email": "demo@pingpongx.com",
                "domain": "pingpongx.com",
                "phone": "15988890852",
                "mobile": "15988890856",
                "workPhone": "15988890852",
                "identificationType": "ID",
                "identificationId": "330102199003070115",
                "registerTime": "20191101122000",
                "registerIp": "222.126.52.23",
                "registerTerminal": "PC",
                "registerCountry": "US",
                "registerRange": "1",
                "orderTime": "20191201122000",
                "orderIp": "222.126.52.23",
                "orderCountry": "US",
                "payIp": "222.126.52.23",
                "payCountry": "US",
                "loginTime": "20200427122000",
                "loginIp": "222.126.52.23",
                "lastPayTime": "20200427122000",
                "acquisitionChannel": "Seach engine",
                "firstOrder": "N",
                "nonMemberOrder": "N",
                "preferentialOrder": "N",
                "birthDate": "20000212",
                "customerStatus": "EXISTING"
            },
            "goods": [
                {
                    "name": "Macaron",
                    "description": "Colorful macaron",
                    "sku": "20191201331",
                    "averageUnitPrice": "20",
                    "number": "1",
                    "virtualProduct": "N"
                }
            ],
            "shipping": {
                "firstName": "James",
                "lastName": "LeBron",
                "phone": "13588185079",
                "email": "demo@pingpognx.com",
                "street": "1986 Broad Street",
                "postcode": "35222",
                "city": "Birmingham",
                "state": "Alabama",
                "country": "US",
                "lastModifierStreetTime": "20191225162010",
                "lastModifierPhoneTime": "20191225162010"
            },
            "billing": {
                "firstName": "James",
                "lastName": "LeBron",
                "phone": "13588185079",
                "email": "demo@pingpognx.com",
                "street": "1986 Broad Street",
                "postcode": "35222",
                "city": "Birmingham",
                "state": "Alabama",
                "country": "US"
            },
            "ecommerce": {
                "freeShipping": "N",
                "shippingMethod": "sea"
            }
        },
        "notificationUrl": "http://127.0.0.1:8010/demo/callback/checkoutCallback",
        "merchantUserId": "UN00000001",
        "remark": "demo-checkout-normal"
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    <?php
    
    $curl = curl_init();
    
    curl_setopt_array($curl, array(
      CURLOPT_URL => 'https://%7Bhost%7D/v2/checkout',
      CURLOPT_RETURNTRANSFER => true,
      CURLOPT_ENCODING => '',
      CURLOPT_MAXREDIRS => 10,
      CURLOPT_TIMEOUT => 0,
      CURLOPT_FOLLOWLOCATION => true,
      CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
      CURLOPT_CUSTOMREQUEST => 'POST',
      CURLOPT_POSTFIELDS =>'{
        "sign": "3F9DF2F986EFCE55919C2CA991689D4C",
        "signType": "MD5",
        "accId": "2018092714313010016291",
        "amount": "20",
        "currency": "USD",
        "merchantTransactionId": "MTN193495030728",
        "paymentType": "SALE",
        "shopperResultUrl": "http://127.0.0.1:8010/demo/checkoutResult",
        "threeDSecure": "N",
        "riskInfo": {
            "device": {
                "orderTerminal": "01",
                "fingerprintId": "e10adc3949ba59abbe56e057f20f883e"
            },
            "customer": {
                "customerId": "UN00000001",
                "firstName": "James",
                "lastName": "LeBron",
                "email": "demo@pingpongx.com",
                "domain": "pingpongx.com",
                "phone": "15988890852",
                "mobile": "15988890856",
                "workPhone": "15988890852",
                "identificationType": "ID",
                "identificationId": "330102199003070115",
                "registerTime": "20191101122000",
                "registerIp": "222.126.52.23",
                "registerTerminal": "PC",
                "registerCountry": "US",
                "registerRange": "1",
                "orderTime": "20191201122000",
                "orderIp": "222.126.52.23",
                "orderCountry": "US",
                "payIp": "222.126.52.23",
                "payCountry": "US",
                "loginTime": "20200427122000",
                "loginIp": "222.126.52.23",
                "lastPayTime": "20200427122000",
                "acquisitionChannel": "Seach engine",
                "firstOrder": "N",
                "nonMemberOrder": "N",
                "preferentialOrder": "N",
                "birthDate": "20000212",
                "customerStatus": "EXISTING"
            },
            "goods": [
                {
                    "name": "Macaron",
                    "description": "Colorful macaron",
                    "sku": "20191201331",
                    "averageUnitPrice": "20",
                    "number": "1",
                    "virtualProduct": "N"
                }
            ],
            "shipping": {
                "firstName": "James",
                "lastName": "LeBron",
                "phone": "13588185079",
                "email": "demo@pingpognx.com",
                "street": "1986 Broad Street",
                "postcode": "35222",
                "city": "Birmingham",
                "state": "Alabama",
                "country": "US",
                "lastModifierStreetTime": "20191225162010",
                "lastModifierPhoneTime": "20191225162010"
            },
            "billing": {
                "firstName": "James",
                "lastName": "LeBron",
                "phone": "13588185079",
                "email": "demo@pingpognx.com",
                "street": "1986 Broad Street",
                "postcode": "35222",
                "city": "Birmingham",
                "state": "Alabama",
                "country": "US"
            },
            "ecommerce": {
                "freeShipping": "N",
                "shippingMethod": "sea"
            }
        },
        "notificationUrl": "http://127.0.0.1:8010/demo/callback/checkoutCallback",
        "merchantUserId": "UN00000001",
        "remark": "demo-checkout-normal"
    }',
      CURLOPT_HTTPHEADER => array(
        'Content-Type: application/json'
      ),
    ));
    
    $response = curl_exec($curl);
    
    curl_close($curl);
    echo $response;
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
      OkHttpClient client = new OkHttpClient().newBuilder()
      .build();
    MediaType mediaType = MediaType.parse("application/json");
    RequestBody body = RequestBody.create(mediaType, "{\n    \"sign\": \"3F9DF2F986EFCE55919C2CA991689D4C\",\n    \"signType\": \"MD5\",\n    \"accId\": \"2018092714313010016291\",\n    \"amount\": \"20\",\n    \"currency\": \"USD\",\n    \"merchantTransactionId\": \"MTN193495030728\",\n    \"paymentType\": \"SALE\",\n    \"shopperResultUrl\": \"http://127.0.0.1:8010/demo/checkoutResult\",\n    \"threeDSecure\": \"N\",\n    \"riskInfo\": {\n        \"device\": {\n            \"orderTerminal\": \"01\",\n            \"fingerprintId\": \"e10adc3949ba59abbe56e057f20f883e\"\n        },\n        \"customer\": {\n            \"customerId\": \"UN00000001\",\n            \"firstName\": \"James\",\n            \"lastName\": \"LeBron\",\n            \"email\": \"demo@pingpongx.com\",\n            \"domain\": \"pingpongx.com\",\n            \"phone\": \"15988890852\",\n            \"mobile\": \"15988890856\",\n            \"workPhone\": \"15988890852\",\n            \"identificationType\": \"ID\",\n            \"identificationId\": \"330102199003070115\",\n            \"registerTime\": \"20191101122000\",\n            \"registerIp\": \"222.126.52.23\",\n            \"registerTerminal\": \"PC\",\n            \"registerCountry\": \"US\",\n            \"registerRange\": \"1\",\n            \"orderTime\": \"20191201122000\",\n            \"orderIp\": \"222.126.52.23\",\n            \"orderCountry\": \"US\",\n            \"payIp\": \"222.126.52.23\",\n            \"payCountry\": \"US\",\n            \"loginTime\": \"20200427122000\",\n            \"loginIp\": \"222.126.52.23\",\n            \"lastPayTime\": \"20200427122000\",\n            \"acquisitionChannel\": \"Seach engine\",\n            \"firstOrder\": \"N\",\n            \"nonMemberOrder\": \"N\",\n            \"preferentialOrder\": \"N\",\n            \"birthDate\": \"20000212\",\n            \"customerStatus\": \"EXISTING\"\n        },\n        \"goods\": [\n            {\n                \"name\": \"Macaron\",\n                \"description\": \"Colorful macaron\",\n                \"sku\": \"20191201331\",\n                \"averageUnitPrice\": \"20\",\n                \"number\": \"1\",\n                \"virtualProduct\": \"N\"\n            }\n        ],\n        \"shipping\": {\n            \"firstName\": \"James\",\n            \"lastName\": \"LeBron\",\n            \"phone\": \"13588185079\",\n            \"email\": \"demo@pingpognx.com\",\n            \"street\": \"1986 Broad Street\",\n            \"postcode\": \"35222\",\n            \"city\": \"Birmingham\",\n            \"state\": \"Alabama\",\n            \"country\": \"US\",\n            \"lastModifierStreetTime\": \"20191225162010\",\n            \"lastModifierPhoneTime\": \"20191225162010\"\n        },\n        \"billing\": {\n            \"firstName\": \"James\",\n            \"lastName\": \"LeBron\",\n            \"phone\": \"13588185079\",\n            \"email\": \"demo@pingpognx.com\",\n            \"street\": \"1986 Broad Street\",\n            \"postcode\": \"35222\",\n            \"city\": \"Birmingham\",\n            \"state\": \"Alabama\",\n            \"country\": \"US\"\n        },\n        \"ecommerce\": {\n            \"freeShipping\": \"N\",\n            \"shippingMethod\": \"sea\"\n        }\n    },\n    \"notificationUrl\": \"http://127.0.0.1:8010/demo/callback/checkoutCallback\",\n    \"merchantUserId\": \"UN00000001\",\n    \"remark\": \"demo-checkout-normal\"\n}");
    Request request = new Request.Builder()
      .url("https://{host}/v2/checkout")
      .method("POST", body)
      .addHeader("Content-Type", "application/json")
      .build();
    Response response = client.newCall(request).execute();
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // Make sure to add code blocks to your code group

    # 签名串示例

    signContent:accId=2018092714313010016291&amount=20&currency=USD&merchantTransactionId=MTN193495030728&notificationUrl=http://127.0.0.1:8010/demo/callback/checkoutCallback&shopperResultUrl=http://127.0.0.1:8010/demo/checkoutResult&signType=MD5
    
    1

    # 响应参数

    参数字段 参数属性 参数说明
    clientId M PingPong 商户商户号
    accId M PingPong 商户店铺编号
    merchantTransactionId M 商户网站的的交易流水号
    code M 结果状态码
    description M 结果描述
    token M 收银台模式下,本次结账请求的唯一标示
    paymentUrl M PingPong 支付收银台地址
    innerJsUrl M PingPong 内嵌 JS 文件内容地址
    signType M 签名规约,支持 MD5、SHA256,具体⻅本文“签名规约” 一栏
    sign M 签名内容,具体⻅本文“签名规约”一栏
    remark O 商户扩展字段
    paymentHtml O 预留字段

    # 响应示例

    {
        "accId": "2018092714313010016291",
        "clientId": "2018092714313010016",
        "code": "001000",
        "description": "Successful request",
        "innerJsUrl": "https://pay-cdn.pingpongx.com/production/static/sdk/ppPay.min.js?token=vr_YVR8u7rn7C1gG97DOg5W0OxzazxNYIE56ShWjA4lJrY4wchTnb47oNmp-9ubP",
        "merchantTransactionId": "MTN193495030728",
        "paymentUrl": "https://sandbox-pay-checkout.pingpongx.com/index.html?token=vr_YVR8u7rn7C1gG97DOg5W0OxzazxNYIE56ShWjA4lJrY4wchTnb47oNmp-9ubP",
        "sign": "06D1C606847FD77CC70AAEB94A2A40D6",
        "signType": "MD5",
        "token": "vr_YVR8u7rn7C1gG97DOg5W0OxzazxNYIE56ShWjA4lJrY4wchTnb47oNmp-9ubP"
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

    # 响应加签串示例

    signContent:accId=2018092714313010016291&clientId=2018092714313010016&code=001000&description=Successful request&innerJsUrl=https://pay-cdn.pingpongx.com/production/static/sdk/ppPay.min.js?token=vr_YVR8u7rn7C1gG97DOg5W0OxzazxNYIE56ShWjA4lJrY4wchTnb47oNmp-9ubP&merchantTransactionId=MTN193495030728&paymentUrl=https://sandbox-pay-checkout.pingpongx.com/index.html?token=vr_YVR8u7rn7C1gG97DOg5W0OxzazxNYIE56ShWjA4lJrY4wchTnb47oNmp-9ubP&signType=MD5&token=vr_YVR8u7rn7C1gG97DOg5W0OxzazxNYIE56ShWjA4lJrY4wchTnb47oNmp-9ubP
    
    1

    # 唤起收银台

    # 引入JS-SDK

    复制以下代码,通过CDN地址引入PingPongPay JS-SDK

    <script src="https://pay-cdn.pingpongx.com/production/static/sdk/1.2.0/ppPay.min.js"></script>
    
    1

    # 初始化并SDK并渲染收银台

    # 初始化SDK示例代码

    <script>
    window.onload = function() {// dom 加载完毕后初始化
    var client = new ppPay({
    lang: 'zh', // 语言 - 中文(zh)和英文(en) (可选参数) root: "#ufo",
    // 支付界面绑定的 div id (可选参数)(如果没有 root,收银台会自动插入 dom 到 body 中) manul: false, // 手动模式。 开启后可使用 client.actionPayment()方法调用支付 located: true, // 是否显示 located(默认为 true)
    showPrice: true,// 是否展示价格
    bill: true, // 是否显示账单名 (默认为 true)
    mode: 'test', // 本地测试模式 sandbox test build (沙箱,测试,线上) (必填参数)、 menu: false, // 是否开启菜单侧边栏(如果接入了本地化,可通过开启次选项,显示多
    个支付方式,默认 false)
    base: { // 弹框样式配置
    // price
    priceBgColor: "#fff", priceFontColor: "#1FA0E8", priceFontSize: "32px",
     // 框体样式
    width: '500px', // 框体宽度
    height: '800px', // 框体高度
    fontSize: '14px', // 整体字体大小
    backgroundColor: '#fff', // 整体弹框背景色
    borderRadius: '0px',
    borderWidth: '1px',
    borderColor: 'transparent', // 弹边框颜色 transparent 为透明,支持 rgba 和 hex maskBackgroundColor: 'rgba(0,0,0,.5)',
    // 全屏展示时背景颜色和透明度 rgba 格式
    maskzIndex: '100',
    // 全屏展示时的层级(如果有 div 没有被背景覆盖,可调高次数值)
    // loading 动画
    loadingColor: '#20a0e8', // 加载动画颜色
    loadingBackgroundColor: 'rgba(255,255,255,0.8)', // 动画背景色,rgba 格式 //bill 定制 css
    billPadding: '0px',
    billColor: 'rgba(0, 0, 0, 0.4)',
    billFontSize: '14px',
    //located 定制 css
    locatedPadding: '20px 0',
    locatedColor: "#999999",
    locatedBgColor: '#eee',
    locatedFontSize: '14px',
    // 弹框头部
    showHeader: true, // 是否展示头部
    showHeaderLabel: true, // 是否展示头部字体 headerLabelFont: "Credit Card", // 自定义头部字体内容 headerColor: '#333333', // 头部字体颜色
    headerSize: '16px', // 头部字体大小 headerBackgroundColor: '#fff', // 头部背景色 headerPadding: '20px',
    headerfontWeight: '400', // 粗体或细体
    // 按钮 button
    btnSize: '100%', // 按钮宽度百分比或者 px btnColor: '#fff', // 按钮字体颜色 btnFontSize: '14px', // 按钮字体大小
     btnPaddingX: '20px', // 字体与宽体左右间距 btnPaddingY: "10px", // 顶部和底部间距 btnBackgroundColor: '#1fa0e8', // 按钮背景色 btnBorderRadius: '4px', // 按钮圆⻆ btnBorderColor: '#1fa0e8',
    // 可单独配置 btn 边框颜色覆盖 btnBorder 中设置的颜色
    btnMarginTop: '0px', // button 离顶部距离 }
    })
    // 请求 checkout 接口获取 token var sdkConfig = {
    //token: token // token 为请求后台获取到的支付凭证 }
    // 拿到 token 后传入
    client.createPayment(sdkConfig) )
    })
    // manul 模式
    // 开启 manul 模式 意味着 收银台不会生成支付按钮,支付需要通过实例中的
    actionPayment 方 法进行手动支付 // 使用方法
    function pay() { client.actionPayment()
    }
    //在点击事件中执行 pay()方法即可支付// 需要将此方法绑定到你自定义的按钮上去 用户点击
    按钮后触发方法 //FAQ
    //使用时请查看 ppPay 是否是最新版本
    //可在浏览器 console 中输入 ppPay.version 查看 (最新版本为:1.2.0) </script>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59

    # PHP调用示例代码

    <!doctype html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport"
              content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>Document</title>
        <style>
            #ufo-container{
                width: 545px;
                min-height: 100vh;
                margin: 0 auto;
            }
        </style>
    </head>
    <body>
    <div id="ufo-container"
         style="z-index: 999;">
    </body>
    </html>
    <?php
    
    class demo
    {
        protected string $accId = '2018092714313010016291';
        protected string $salt = 'F78BC96A55548B2319EE68E0';
        protected string $signType = 'MD5';
        protected string $gateway = 'https://sandbox-acquirer-payment.pingpongx.com';
    
        /**
         * @throws Exception
         */
        public function checkout()
        {
            $data = [
                "accId" => $this->accId,
                "amount" => "2",
                "currency" => "USD",
                "merchantTransactionId" => rand() . date("YmdHiss"),
                "paymentType" => "SALE",
                "shopperResultUrl" => "www.baidu.com",
                "shopperCancelUrl" => "www.baidu.com",
                "notificationUrl" => "www.baidu.com",
                "signType" => $this->signType,
                "sign" => "",
                "language" => "en",
                "threeDSecure" => "N",
                "riskInfo" => [
                    "customer" => [
                        "customerId" => null,
                        "firstName" => "Patricia",
                        "lastName" => "Stryker",
                        "email" => "123@test.com",
                        "phone" => "18989541256",
                        "registerTime" => date("y-m-d"),
                        "registerIp" => $_SERVER['REMOTE_ADDR'],
                        "registerTerminal" => 'PC',
                        "registerRange" => '1',
                        "orderTime" => date("y-m-d H"),
                        "orderIp" => $_SERVER['REMOTE_ADDR'],
                        "payIp" => $_SERVER['REMOTE_ADDR'],
                    ]
                ],
                "goods" => [
                    "name" => "测试商品",
                    "description" => "这是测试商品",
                    "sku" => "白色12GB测试商品",
                    "averageUnitPrice" => "2.45",
                    "number" => "2",
                    "imgUrl" => "www.baidu.com/img/test.img",
                    "virtualProduct" => "N",
                ],
                "shipping" => [
                    "firstName" => "Patricia",
                    "lastName" => "Stryker",
                    "phone" => "18989841256",
                    "email" => "123@test.com",
                    "street" => "3630 Wolf Pen Road",
                    "postcode" => "94612",
                    "city" => "Oakland",
                    "state" => "CA",
                    "country" => "US",
                    "lastModifierStreetTime" => date(""),
                    "lastModifierPhoneTime" => date(""),
                ],
                "billing" => [
                    "firstName" => "Patricia",
                    "lastName" => "Stryker",
                    "street" => "3630 Wolf Pen Road",
                    "postcode" => "94612",
                    "city" => "Oakland",
                    "state" => "CA",
                    "country" => "US",
                ],
                "eCommerce" => [
                    'freeShipping' => "",
                    'shippingMethod' => "",
                ]
            ];
            $signData = self::getRequestSignatureScopeWhenRequest($data);
    //        var_dump($signData);
            $sign = $this->getSign($signData, $this->salt);
            $data['sign'] = $sign;
            $requestUrl = $this->gateway . "/v2/checkout/";
            $response = $this->request($requestUrl, $data);
    //        var_dump($response);
            $this->getCheckoutBar($response['token']);
        }
    
    
        /**
         * 返回签名参数范围
         * @param array $params
         * @return string[]
         */
        public static function getRequestSignatureScopeWhenRequest(array $params): array
        {
            $scope = [
                'clientId',
                'accId',
                'transactionId',
                'merchantTransactionId',
                'amount',
                'currency',
                'notificationUrl',
                'shopperResultUrl',
                'signType',
            ];
    
            $inScope = [];
            foreach ($params as $key => $param) {
                if (in_array($key, $scope, true)) {
                    $inScope[$key] = $param;
                }
            }
    
            return $inScope;
        }
    
    
        /**
         * @param array $data
         * @param string $salt
         * @param int $isLower
         * @return string
         * @throws Exception
         */
        public function getSign(array $data, string $salt, int $isLower = 0): string
        {
            ksort($data);
            $signStr = $salt;
            foreach ($data as $key => $value) {
                $signStr .= "{$key}=$value&";
            }
    
            $signStr = rtrim($signStr, '&');
    //        var_dump("signStr:",$signStr);
            $sign = md5($signStr);
            if ($isLower === 0) {
                return $sign;
            }
    
            if ($isLower === 1) {
                return strtolower($sign);
            }
    
            if ($isLower === 2) {
                return strtoupper($sign);
            }
    
            throw new Exception('param isLower error');
        }
        /**
         * @param string $url
         * @param array $data
         * @return array
         * @throws Exception
         */
        public function request(string $url, array $data): array
        {
            $param = json_encode($data, JSON_UNESCAPED_UNICODE);
    
            $ch = curl_init($url);
            curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
            curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17');
            curl_setopt($ch, CURLOPT_POSTFIELDS, $param);//send values
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_POST, 1);
            curl_setopt($ch, CURLOPT_TIMEOUT, 50); //timeout in seconds
            //curl_setopt($ch,CURLOPT_FOLLOWLOCATION, 1);
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
            // Set HTTP Header for POST request
            curl_setopt($ch, CURLOPT_HTTPHEADER, [
                'Content-Type: application/json',
                'Content-Length: ' . strlen($param)]);
            $responseStr = curl_exec($ch);
            $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    
            if ($httpCode >= 299) {
                throw new Exception("request filed httpCode {$httpCode}");
            }
    
            if (empty($responseStr)) {
                throw new Exception('empty response');
            }
    
            $responseArr = json_decode($responseStr, true);
            if (empty($responseArr)) {
                throw new Exception('response is not json');
            }
    
            return $responseArr;
        }
    
    
        public function rand()
        {
    
            $strs = "QWERTYUIOPASDFGHJKLZXCVBNM";
    
            $name = substr(str_shuffle($strs), mt_rand(0, strlen($strs) - 11), 4);
            return $name;
        }
    
        public function getCheckoutBar(string $token)
        {
            $js = '<div id="ufo" style="width: 100%;height: 100%"></div><div><script src="https://pay-cdn.pingpongx.com/production/static/sdk/1.2.0/ppPay.min.js"> </script>';
            $js .= "<script>
                        window.onload = function() {
                          
                            let client = new ppPay({
                                lang: 'en',
                                root: '#ufo',
                                manul: false,
                                located: true,
                                bill: true,
                                mode: 'sandbox', // 本地测试模式 sandbox test build (沙箱,测试,线上) (必填参数)、
                                base: { // 弹框样式配置 (可选参数)
                                    width: '100%', // 弹框宽度
                                    height: '100%', // 弹框⾼度 (当⾼度不指定时 loading可能会失效)
                                    formColor: '#606266', // 表单输入框文字颜色
                                    formInputSize: '14px', // 表单输入框文字大小
                                    formInputHeight: '36px', // 表单高度
                                    formInputLineHeight: '34px', // 表单line height
                                    formInputMarginTop: '0px', // input 与顶部距离
                                    formBorderColor: '#E4E7ED', // 表单边框颜色[color,none,0]
                                    formBorderRadius: '4px', // 表单框体圆角(50%为半圆)
                                    formBackgroundColor: '#fbfbfb', // 表单框体颜色
                                    formInputBackgroundColor: '#fff', // 表单input框体颜色
                                    showHeader: true,
                                    showHeaderLabel: true
                                }
                            })
                            // request PingPong api for token
                            let token = '" . $token . "';
                            let sdkConfig = {
                                token: token
                            }
                                // 拿到token后传⼊
                            client.createPayment(sdkConfig)
                            setPPPayPropWin();
                            function setPPPayPropWin(){
                                       let winWidth = window.innerWidth|| document.documentElement.clientWidth|| document.body.clientWidth;
                                       let winHeight = window.innerHeight|| document.documentElement.clientHeight|| document.body.clientHeight;
                                       let ufoContainer = document.getElementById('ufo-container');
                                       ufoContainer.style.height = (winHeight/2)+'px';
                                       ufoContainer.style.width = (winWidth/2)+'px';
                                      if (winWidth>= 500){
                                           let clientW = Math.floor(winWidth/3);
                                           clientW = clientW>=500?clientW:500;
                                           ufoContainer.style.width = clientW+'px';
                                      }else {
                                           ufoContainer.style.width = winWidth+'px';
                                      }
                                }
                        }
                     </script>";
            $js .= '</div>';
            $js .= '</div>';
            echo $js;
        }
    
    }
    
    (new demo())->checkout();
    
    ?>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289

    Tips :

    如上所示,sdk初始化条件为:

    1. 必须从v2/checkout接口中获取到token 传入其中。

    2. 必须有一个根元素(ufo-container)来确定收银台渲染的位置。



    注意 :

    1. 参数mode必须正确填写,开发测试sandbox,上线之后使用build,应根据AccId自动判断,决不允许上线使用sandbox,否则交易将不会扣款,造成资损

    2. lang 应该根据浏览器语言首选项自适应,获取HTTP header的Accept-Language来做映射

    3. 收银台应该正常展示,不能有遮挡

    4. located,bill默认强制展示,否则影响交易

    5. 收银台不支持无痕模式


    # 完成付款

    正确如上设置之后,应正常渲染出收银台,正确输入卡号,过期时间和CVV信息,即可完成交易。

    沙箱模式下的收银台如下所示

    # 3DS

    收银台模式下,3DS 流程由 pingpong checkout 进行了内部封装,无需商户额外接入。

    # 预授权

    # 什么是预授权

    • 商户在持卡人消费前先冻结持卡人creditcard的余额或者额度。
    • 持卡人消费结束后,商户再正式扣掉这部分资金,常用于酒店住宿、出租等行业。
    • 线上交易正常预授权资金冻结期限为7天,部分发卡行是30天。

    注意 :
    • PingPongPay默认不会自动解冻持卡人资金。
    • 预授权交易7天或者30天后,如果商户在这期间没有任何操作,发卡行会自动解冻持卡人冻结的资金。
    • 商户发起Auth之后必须发起在恰当时间(通常为7天)发起CAPTURE,否则交易将被超时取消

    # 如何发起预授权交易

    v2/checkout 接口中

    交易类型:

    • SALE-直接付款
    • AUTH-预授权

    填入paymentType= AUTH 即视为AUTH 业务

    # CAPTURE

    # 什么是CAPTURE

    对已经预授权成功的交易,在资金冻结期限内使用预授权完成进行请款操作。

    # 业务前提

    针对“预授权”交易可以发起“预授权完成”操作。

    # 业务限制

    1. 当前“预授权”交易未被判定为“预授权取消”。
    2. 预授权完成的金额需小于等于关联的 CAPTURE 交易。

    # 如何发起

    收银台模式和端到端模式都请求二次交易接口,填入paymentType=CAPTURE发起退款

    # 接口地址

    https://{host}/v2/payment/{transactionId}
    
    1

    参数详见 退款预授权

    # VOID

    # 什么是VOID

    对已经预授权的交易,通知发卡行进行预授权撤销,预授权撤销成功后发卡行会解冻持卡人冻结的资金。

    # 业务前提

    针对“预授权”交易可以发起“预授权撤销”操作。

    # 业务限制

    1. 当前“预授权”交易未被判定为“预授权完成”。
    2. 预授权撤销只能全额撤销。

    # 如何发起

    收银台模式和端到端模式都请求二次交易接口,填入paymentType=VOID发起退款

    # 接口地址

    https://{host}/v2/payment/{transactionId}
    
    1

    参数详见退款预授权

    上次更新: 2024/01/12, 17:42:45
    3D集成指南V2(风控插件)
    跳转接入

    ← 3D集成指南V2(风控插件) 跳转接入→

    杭州乒乓智能技术有限公司 | Copyright © 2015-2024 All rights reserved. pay.pingpongx.com
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式