内嵌接入
# 内嵌接入
# 收银台模式系统交互流程
以下对主要步骤做简要说明
- 客户端提交订单给商户服务端处理
- 商户服务端返回PingPongCheckout JS-SDK需要的参数(包含订单信息,签名和JS-SDK初始化需要的参数)
- 客户端初始化PingPongCheckout SDK,并且调用`createPayment`,传入订单信息。
- SDK自动开始和PingPongCheckout服务端交互,成功之后将会渲染PingPongCheckout收银台
- 买家填写卡号和cvv等支付信息
- 提交支付信息
- 如果是3D交易,还需要3D验证,否则直接展示交易结果
- 图例:
- 7.1 3D流程
- 7.2 非3D流程
- 异步通知详见如何获取交易状态
- 收到异步通知需要响应OK给PingPongCheckout
# 服务端接入
商户客户端在请求服务端下单之后,需要准备下列参数给客户端使用。
# 准备客户端需要的参数
以下是SDK所需要的交易单参数。应从商户服务端下发,给SDK使用。
详见 收银台预下单
# 请求参数签名
填充上述参数之后,还需要对参数进行签名,详见 签名规约 获取到签名之后,将参数返回给客户端,客户端在使用SDK时候会需要订单参数来请求PingPongCheckout。
# 客户端接入
# 引入JS-SDK
复制以下代码,通过CDN地址引入PingPongCheckout JS-SDK
<script src="https://pay-cdn.pingpongx.com/production-fra/static/sdk-v3/ppPay.min.js"></script>
# SDK-config示意图
# JS-SDK收银台语种支持
在SDK
lang
参数中配置 默认英语
更多语言详见Locale
# 初始化SDK示例代码
客户端点击下单按钮,提交订单之后,从服务端获取订单相关参数(order 对象),在初始化PingPongCheckout JS-SDK之后,传入order对象 到createPayment方法中,开始和PingPongCheckout交互,成功之后将会渲染出PingPongCheckout收银台
window.addEventListener('load', loadHandle);
function loadHandle() {
const conf = {
mode: 'sandbox', //sandbox/production (必填参数)
lang: 'en', // 语言,不传默认英语 - de德语 en英语 es西班牙语 fr法语 it意大利语 ru俄语 zh中文 jp日语
root: '#payment-wrap', // PingPong收银台在哪个元素上渲染 须为id (必填)
manul: false, // 手动模式。 即:支付按钮需要自己渲染,开启后可使用client.actionPayment()方法调用支付
showPrice: true, // 是否展示价格
bill: true, // 是否显示bill
located: true, // 是否显示located(默认为true)
menu: true, //是否显示支付方式按钮
base: {
width: '100%', // 收银台宽度
height: '800px', // 收银台高度
backgroundColor: '#fff', // 收银台整体背景色
// 顶部价格及币种
priceBgColor: '#fff', // 顶部价格区域背景色
priceFontColor: '#1fa0e8', // 顶部价格字体颜色
priceFontSize: '32px', // 顶部价格字体大小
priceMB: '10px', // 顶部价格区域距底部间距
// 引导标题: “How would you like to pay”
showHeaderLabel: true, // 是否显示引导标题
headerMB: '24px', // 引导标题距底部间距
headerSize: '20px', // 引导标题字体大小
headerColor: 'rgba(0, 0, 0, 0.85)', // 引导标题字体颜色
headerfontWeight: 700, // 引导标题加粗
// 支付方式
payMethodsWidth: '240px', // 支付方式按钮宽度
payMethodsHeight: '48px', // 支付方式按钮高度
payMethodsColor: 'rgba(0, 0, 0, 0.85)', // 支付方式按钮字体颜色
payMethodsActiveColor: '#1fa0e8', // 支付方式按钮点击后字体颜色
payMethodsFontsize: '14px', // 支付方式按钮字体大小
payMethodsActiveBorderColor: '#1fa0e8', // 支付方式按钮点击后边框颜色
payMethodsActiveBorderShadow: '0 2px 8px 0 rgb(31 160 232 / 20%)', // 支付方式按钮点击后阴影
showIcons: true, // 是否显示卡图标
showHeaderAmount: true, // 是否显示头部支付金额
// 表单相关
formItemMargin: '16px', // 表单距底部间距
formItemBorderColor: 'rgba(0, 0, 0, 0.08)', // 表单边框颜色
formItemFocusColor: '#1fa0e8', // 表单聚焦时提示文案和边框颜色
// 支付按钮
btnSize: '100%', // 按钮宽度百分比或者px
btnColor: '#fff', // 按钮字体颜色
btnFontSize: '14px', // 按钮字体大小
btnMarginTop: '10px', // button 距顶部距离
btnMarginBottom: "24px", // button 距底部距离
btnBackgroundColor: '#1fa0e8', // 按钮背景色
btnBorderRadius: '8px', // 按钮圆角
btnBorderColor: '#1fa0e8', // 可单独配置btn边框颜色覆盖btnBorder中设置的颜色
}
}
let client = new ppPay(conf);
//order对象从服务端获取
const order = {
requestId: "唯一请求ID",
accId: "商户店铺ID",
amount: "交易金额,保留两位有效数字,小数部分补零(%.02f)",
merchantUserId: "商户端持卡人ID",
currency: "交易币种,ISO 4217 三位币种,具体支持币 种⻅附件《交易币种》",
interactiveMode: "接入方式:checkout",
language: "收银台语种 默认为 en",
merchantTransactionId: "商户网站交易流水号,每次请求的唯一标 识,可用于后续订单查询和对账",
paymentType: "交易类型: DEBIT-直接付款 AUTH-预授权",
notificationUrl: "异步通知地址",
shopperCancelUrl: "收银台页面取消支付操作时页面跳转地址",
shopperResultUrl: "商户自定义接收重定向的结果 URL;如 3DS 验证,银行在线转账或虚拟钱包之类支 付方式时,最后需要重定向到商户指定的⻚面地址",
threeDSecure: "是否指定当前交易强制走 3DS 交易(Y/N) 默认为“N”即不主动走 3DS 交易。注意:商户需要配置自主 3DS 策略,该参数 才生效",
primaryMerchantTransactionId:"原始商户交易订单号(第一次交易使用的订单号, token分批支付时必填)",
periodsNum:"分批次数(toke分批支付时必填)",
paymentBrand:"支付方式,如指定 Visa、MasterCard 等国 际信用卡,或者指定本地支付方式如\nGiroPay;具体支持的支付方式⻅附录《支付 方式》",
signType: "签名规约,支持 MD5、SHA256,具体⻅本文 签名规约一栏",
sign: "签名内容,具体⻅本文“签名规约”一栏",
airline: {//航空行业信息
adultsNumber: "成人人数",
aircraftCabinType: "舱位类型,nFirst Class/Business\nClass/Premium Economy (高端经济舱)/Economy (普通经济舱)",
airlineCompanyID: "航空公司标识码",
airlinePnr: "航空公司 PNR",
arrivalCity: "到达城市",
arrivalCountry: "到达国家,(ISO 二字码)⻅ 附录《国家代码》",
arrivalTime: "航班抵达时间 (yyyyMMddHH)",
arrivalTimezone: "到达时区",
babyNumber: "婴儿人数",
bestFare: "航司增值(Y/N)",
bookChannel: "订票渠道: Web-网站\ncall center-售票中心 mobile app-手机端 ota-在线旅行平",
changedTakeoffTime: "航班改签后的起⻜时间 (yyyyMMddHH)",
childrenNumber: "儿童人数",
connectingCity: "当途经多个中转站时,需按 顺序传多个字段,例如:\n途径南京、北京 2 个中转站 时: \"connectingCity1\: \" 南京\n\", \"connectingCity2\: \"北 京\"",
connectionTicket: "是否套票 Y-是 N-否",
contactEmail: "订单填写的联系电子邮箱",
contactName: "First Name+Last Name",
contactPhone: "订单填写手机号",
flightNumber: "订单填写的联系号码,去掉 \"+\"或者\"-\"等符号, 只需要数据部分",
freeChange: "是否免费 Y-是 N-否",
frequentFlyerNumber: "常旅号, 没有常旅号的直接 不传此字段",
haveChanged: "预定的机票是否已改签过 (Y/N)\nY 代表变更机票,即当前 为改签订单;\nN 代表初始售票机票,即 未改签。",
hotel: {//旅店
bookNumber: "预定客房数量",
bookRange: "预定间夜数量",
checkInDate: "入住日期",
city: "酒店所在城市",
country: "酒店所在国家",
customerName: "住宿客户名称",
name: "酒店名称",
refundPolicy: "退款政策 免费取消、部分退款、不退款",
star: "星级"
},
ifConnectingFlight: "是否中转(Y/N)",
ifRoundtripFlight: "是否往返航班(Y/N)",
insurance: "是否购买保险(Y/N)",
leaveRange: "距出发时间有多少小时",
passengers: [//乘客信息
{
birthday: "乘客生日",
firstName: "乘客名称",
identificationId: "乘客证件号",
identificationType: "证件类型",
lastName: "乘客姓",
nationality: "乘客国籍",
phone: "乘客手机号"
}
],
productType: "产品类型 ",
routeType: "航线类型",
takeoffCity: "出发城市",
takeoffCountry: "出发国家(ISO)",
takeoffTime: "航班起飞时间",
takeoffTimezone: "航班起飞时间对应的时区(UTC+N)",
thirdPartyBook: "持卡人是否包含在乘机人之中(Y/N)",
tripType: "行程类型",
valueAdded: "航司增值服务"
},
billing: {
city: "账单城市",
country: "账单国家",
email: "账单邮箱",
firstName: "账单人名",
lastName: "账单人姓",
phone: "账单手机号",
postcode: "账单地址邮编",
state: "账单地址洲",
street: "账单地址街道"
},
browserInfo: {
language:"购物者浏览器的navigator.language值(如IETF BCP 47中所定义)。",
acceptHeader: "http响应头信息,\n示例值: text/html, application/xhtml+xml, application/xml;q=0.9, image/webp, image/apng, *;q=0.8",
colorDepth: "窗口颜色, 获取浏览器screen.colorDepth\n示例值: 32",
jetLag: "CC",
screenHeight: "窗口高度",
screenWidth: "窗口宽度",
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: "窗口大小:01-250x400 02-390x400 03-500x600 04-600x400 05 - Full screen",
javaEnabled:false,
javaScriptEnabled:true,
javaScriptEnabled:true,
},
carRental: {//租车行业
childrenNumber: "儿童人数",
discountAmount: "用⻋已折扣金额,单位:USD",
discountType: "折扣类型,百分比,如 30% 为七折",
email: "邮箱",
firstName: "付款人名称",
identificationId: "身份证件号",
identificationType: "证件类型 默认:ID",
insuranceLimit: "保险额度",
insuranceType: "保险类型",
lastName: "付款人姓氏",
model: "所用⻋型:small medium large premium-豪⻋ suv truck-卡⻋ van-货箱⻋ convertible-敞篷⻋",
nationality: "用⻋人国籍,(ISO 二字码)⻅附录《国家代码》",
personNumber: "出行人数",
phone: "手机号码",
purchasedInsurance: "是否购买保险 Y-是 N-否",
rentCity: "取⻋城市",
rentCountry: "取⻋国家, (ISO 二字码)⻅附录《国家代码》",
rentTime: "取⻋时间,yyyyMMddHHmmss",
rentalCompany: "还车公司",
returnCity: "还⻋城市",
returnCountry: "还⻋国家, (ISO 二字码)⻅附录《国家代",
returnTime: "还车时间"
},
customer: {//客户信息
acquisitionChannel: "SEARCH_ENGINE",
customerId: "会员ID,持卡人在商户网站的会员ID",
domain: "来源地址",
email: "持卡人注册邮箱",
firstName: "持卡人名称",
firstOrder: "是否首次支付 Y-是 N-否",
identificationId: "持卡人身份证件号码(特定国家需要,如墨西哥)",
identificationType: "持卡人身份证件类型(特定国家需要,如墨西哥) 默认值ID",
lastName: "持卡人姓氏",
lastPayTime: "最后支付时间,yyyyMMddHHmmss",
loginIp: "登陆IP,支持 IPv4 格式和 IPv6 格式",
loginTime: "登陆时间,yyyyMMddHHmmss",
middleName: "中间名称",
nonMemberOrder: "是否会员订单 Y-是 N-否",
orderCountry: "订单国家",
orderIp: "订单IP,支持 IPv4 格式和 IPv6 格式",
orderTime: "订单时间",
payCountry: "支付国家",
payIp: "支付IP,支持 IPv4 格式和 IPv6 格式",
phone: "持卡人联系电话",
preferentialOrder: "优惠订单 Y-是 N-否",
registerCountry: "注册国家",
registerIp: "持卡人注册IP,支持 IPv4 格式和 IPv6 格式",
registerRange: "持卡人账户年龄,注册时长; 单位年,不足1年按1年算",
registerTerminal: "注册终端PC、MOBILE",
registerTime: "持卡人注册时间,yyyyMMddHHmmss",
registerUserEmail: "注册邮箱"
},
device: {//设备信息
orderTerminal: "01-移动端浏览器(app)\n02-PC端浏览器(pc)\n03-3DS请求者启动\n04-SDK(ios)\n05-SDK(Android)"
},
eCommerce: {//电子商务行业信息
freeShipping: "包邮情况,是否包邮 Y-是 N-否",
shippingMethod: "物流方式:railway-铁路 sea-海运 air-空运 highway-公路 MultiTransport-复合交通",
},
goods: [//商品信息
{
averageUnitPrice: "商品平均单价,人民币提现商户必填",
description: "商品描述",
name: "商品名称,人民币提现商户必填",
number: "商品数量,人民币提现商户必填",
sku: "商品编号,产品SKU",
virtualProduct: "是否是虚拟产品,虚拟商品 Y-是 N-否"
}
],
reCharge: {//充值类型
accountId: "充值账户ID",
accountName: "充值账户姓名,被充值的账户姓名",
gameCategory: "游戏充值的类型: Poit Card-点卡 Prop-道具 Other-其他",
gameName: "游戏名称",
gameTerminal: "游戏终端类型:PC-电脑 Mobile-移动端",
platformCategory: "平台充值的类型: Direct-直充 Card-卡密 Gift Card-礼品卡",
serverCountry: "游戏账号所属服务器国家(使用ISO二字码)",
type: "充值类型: Game-游戏充值 Platform-平台充值"
},
shipping: {//送货地址信息
city: "收货人城市",
country: "收货人国家",
email: "账单邮箱",
firstName: "收货人名",
lastName: "收货人姓",
phone: "收货手机",
postcode: "邮编",
state: "洲",
street: "街道",
lastModifierStreetTime: "最后更新街道时间",
lastModifierPhoneTime: "最后更新手机号时间"
}
}
//使用此方法将会创建收银台
client.createPayment(order,()=>{
//成功回调
});
// manul 模式 使用此方法将不会出现支付按钮,支付时机将会取决于持卡人的调用
// client.actionPayment(callback)
}
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
1. 参数mode必须正确填写,开发测试sandbox,上线之后使用build,可以根据AccId自动判断,决不允许上线使用sandbox,否则交易将不会扣款,造成资损
2. lang 应该根据浏览器语言首选项自适应,获取HTTP header的Accept-Language来做映射
3. 收银台应该正常展示,不能有遮挡
4. located,bill默认强制展示,否则影响交易
5. 收银台不支持禁用cookies
# 完成付款
正确如上设置之后,应正常渲染出收银台,正确输入卡号,过期时间和CVV信息,即可完成交易。
沙箱模式下的收银台如下所示
# 重新支付
收银台若未支付成功,可以手动打开URL再次发起支付,一个收银台URL有效是时长为48小时
# 记住卡信息
收银台模式下,PingPongCheckOut 会根据收银台页面勾选框是否勾选来判断是否记住用户卡信息,如果用户点击勾选,就会将用户的merchantUserId和卡号信息进行绑定,后续可以直接根据上送的merchantUserId取对应用户已保存的卡信息展示在收银台页面,用户按需选择卡号进行支付即可。如下图所示:
首次支付-勾选保存卡信息-创建Token
再次支付-自动展示历史卡信息
# 3DS
收银台模式下,3DS 流程由 PingPongCheckout 进行了内部封装,无需商户额外接入。
# 预授权
# 什么是预授权
- 商户在持卡人消费前先冻结持卡人creditcard的余额或者额度。
- 持卡人消费结束后,商户再正式扣掉这部分资金,常用于酒店住宿、出租等行业。
- 线上交易正常预授权资金冻结期限为7天,部分发卡行是30天。
- PingPongCheckout默认不会自动解冻持卡人资金。
- 预授权交易7天或者30天后,如果商户在这期间没有任何操作,发卡行会自动解冻持卡人冻结的资金。
- 商户发起Auth之后必须发起在恰当时间(通常为7天)发起CAPTURE,否则交易将被超时取消
# 如何发起预授权交易
v3/checkout 接口中
交易类型:
- DEBIT-直接付款
- AUTH-预授权
填入paymentType= AUTH 即视为AUTH 业务
# CAPTURE
# 什么是CAPTURE
对已经预授权成功的交易,在资金冻结期限内使用预授权完成进行请款操作。
# 业务前提
针对“预授权”交易可以发起“预授权完成”操作。
# 业务限制
- 当前“预授权”交易未被判定为“预授权取消”。
- 预授权完成的金额需小于等于关联的 CAPTURE 交易。
# 如何发起
收银台模式和端到端模式都请求二次交易接口,填入paymentType=CAPTURE发起退款
# 接口地址
https://sandbox-acquirer-payment.pingpongx.com/v3/payment/{transactionId}
参数详见 退款预授权
# VOID
# 什么是VOID
对已经预授权的交易,通知发卡行进行预授权撤销,预授权撤销成功后发卡行会解冻持卡人冻结的资金。
# 业务前提
针对“预授权”交易可以发起“预授权撤销”操作。
# 业务限制
- 当前“预授权”交易未被判定为“预授权完成”。
- 预授权撤销只能全额撤销。
# 如何发起
收银台模式和端到端模式都请求二次交易接口,填入paymentType=VOID发起退款
# 接口地址
https://sandbox-acquirer-payment.pingpongx.com/v3/payment/{transactionId}
参数详见退款预授权