├── .editorconfig ├── .eslintrc.json ├── .gitignore ├── CHANGELOG.md ├── README.md ├── alipay_in_weixin └── pay.htm ├── demo ├── img │ ├── bgpic.jpg │ ├── logo.png │ └── logo │ │ └── logo-l.svg ├── styles │ └── pinus.css └── views │ └── wap.html ├── dist └── pingpp.js ├── gulpfile.js ├── mods.js.tmpl ├── package.json ├── src ├── agreement.js ├── callbacks.js ├── channels │ ├── abc_pay.js │ ├── abc_pub.js │ ├── alipay.js │ ├── alipay_lite.js │ ├── alipay_pc_direct.js │ ├── alipay_qr.js │ ├── alipay_qr_lakala.js │ ├── alipay_wap.js │ ├── alipay_wap_lakala.js │ ├── bfb_wap.js │ ├── cb_alipay_pc_direct.js │ ├── cb_alipay_wap.js │ ├── cb_wx_pub.js │ ├── ccb_qr.js │ ├── ccb_wap.js │ ├── chinaums_alipay_pub.js │ ├── chinaums_alipay_wap.js │ ├── chinaums_upacp_wap.js │ ├── chinaums_wx_pub.js │ ├── chinaums_wx_wap.js │ ├── cmb_pc_qr.js │ ├── cmb_wallet.js │ ├── commons │ │ └── redirect_base.js │ ├── cp_b2b.js │ ├── extras │ │ ├── ap.js │ │ └── wx_jssdk.js │ ├── isv_lite.js │ ├── isv_wap.js │ ├── jdpay_wap.js │ ├── nucc_b2b_lakala.js │ ├── nucc_b2c_lakala.js │ ├── pab_pc.js │ ├── paypal.js │ ├── qpay_pub.js │ ├── upacp_b2b.js │ ├── upacp_pc.js │ ├── upacp_wap.js │ ├── wx_lite.js │ ├── wx_lite_pab.js │ ├── wx_pub.js │ ├── wx_pub_pab.js │ ├── wx_wap.js │ ├── yeepay_wap.js │ ├── yeepay_wx_lite.js │ ├── yeepay_wx_lite_ofl.js │ ├── yeepay_wx_pub.js │ └── yeepay_wx_pub_ofl.js ├── errors.js ├── init.js ├── libs │ └── md5.js ├── main.js ├── mods.js ├── payment_elements.js ├── stash.js ├── testmode.js ├── transfer_channels │ └── wx_pub.js ├── transfer_elements.js ├── utils.js └── version.js └── test └── test.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | indent_style = space 6 | indent_size = 2 7 | charset = utf-8 8 | 9 | [*.js] 10 | indent_size = 2 11 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "commonjs": true, 5 | "es6": true 6 | }, 7 | "extends": "eslint:recommended", 8 | "rules": { 9 | "indent": [ 10 | "error", 11 | 2, 12 | { 13 | "SwitchCase": 1 14 | } 15 | ], 16 | "linebreak-style": [ 17 | "error", 18 | "unix" 19 | ], 20 | "quotes": [ 21 | "error", 22 | "single" 23 | ], 24 | "semi": [ 25 | "error", 26 | "always" 27 | ], 28 | "no-console": 0, 29 | "max-len": [ 30 | "error", 31 | 80, 32 | 4, 33 | { 34 | "ignoreComments": true, 35 | "ignoreUrls": true 36 | } 37 | ] 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | package-lock.json 3 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # ChangeLog 2 | 3 | ## 2.3.1 4 | 5 | - 更新纯签约支付宝地址为 `https` 6 | - 更新 minimist 到 1.2.6 版本(CVE-2021-44906) 7 | 8 | --- 9 | 10 | ## 2.3.0 11 | - 新增: 支持易宝-微信公众号支付和易宝-微信小程序支付 12 | - 更新: 移除库分期支持 13 | - 更新: 移除杭州银行-微信公众号支付支持 14 | 15 | ## 2.2.27 16 | 17 | - 新增农行 app H5 支付和微信公众号唤起 农行app支付 18 | 19 | --- 20 | 21 | ## 2.2.26 22 | 23 | - 修复支付宝 PC 电脑网站签约跳转问题 24 | 25 | --- 26 | 27 | ## 2.2.25 28 | 29 | - mock 模拟支付地址修改为 `https` 30 | 31 | --- 32 | 33 | ## 2.2.24 34 | 35 | - 新增chinaums_alipay_wap, chinaums_upacp_wap, chinaums_wx_wap, chinaums_alipay_pub, chinaums_wx_pub 36 | 37 | ## 2.2.23 38 | 39 | - 新增 nucc_b2b_lakala,nucc_b2c_lakala 40 | 41 | ### 2.2.22 42 | 43 | - 修正:在部分“浏览器”无法正常打开的异常 44 | 45 | ### 2.2.21 46 | 47 | - 新增: 支持 ccb_wap 48 | 49 | ### 2.2.20 50 | 51 | - 新增:支持 wx_lite_pab 52 | 53 | ### 2.2.19 54 | 55 | - 新增:支持 alipay_wap_lakala, alipay_qr_lakala 56 | 57 | ### 2.2.18 58 | 59 | - 修改:支持 alipay 渠道在支付宝小程序或者支付宝内部网页使用 60 | 61 | ### 2.2.17 62 | 63 | - 新增:pab_pc, wx_pub_pab 渠道 64 | 65 | ### 2.2.16 66 | 67 | - 新增:小程序内使用 alipay 调起支付 68 | 69 | ### 2.2.15 70 | 71 | - 修正:支付宝小程序内报错修正 72 | - 新增:0 元订单直接返回 success 73 | 74 | ### 2.2.14 75 | 76 | - 新增:渠道 `isv_lite` 77 | 78 | ### 2.2.13 79 | 80 | - 新增:渠道 `coolcredit` 81 | - 修正:JSON 解析失败时的报错修正 82 | 83 | ### 2.2.12 84 | 85 | - 新增:渠道 `wx_pub_hzbank` 86 | - 更新:`gulp` 更新至 `4.0.0` 87 | - 更新:移除 `pingpp_ui` 88 | 89 | ### 2.2.11 90 | 91 | - 新增:签约接口 `pingpp.signAgreement(agr, callback)` 92 | 93 | ### 2.2.10 94 | 95 | - 修正:`cb_walipay_wap`、`cb_alipay_pc_direct` 的跳转地址 96 | 97 | ### 2.2.9 (2018-11-29) 98 | 99 | - 更新:新增 `alipay_lite` 渠道 100 | 101 | ### 2.2.7 (2018-11-05) 102 | 103 | - 更新:新增 `ccb_qr` 渠道 104 | 105 | ### 2.2.6 (2018-08-06) 106 | - 更新:判断是否在小程序内支付 107 | 108 | ### 2.2.5 (2018-07-26) 109 | - 新增:添加支付渠道 [ `cmb_pc_qr`] 110 | 111 | ### 2.2.4 (2018-06-22) 112 | - 新增:添加支付渠道 [ `upacp_b2b`] 113 | - 修复:修复 README.md 文档链接 114 | - 更新:更新微信小程序 [`wx_lite`] 115 | 116 | ### 2.2.3 (2018-04-13) 117 | - 新增:添加支付渠道 [ `cb_alipay_pc_direct` , `cb_alipay_wap` , `paypal`] 118 | 119 | ### 2.2.2 (2018-01-22) 120 | - 新增:wx_pub 跨境支付 121 | - 修改:支持小程序模拟支付 122 | 123 | ### 2.2.1 (2018-01-18) 124 | - 新增:`pingpp_ui` 125 | 126 | ### 2.1.16 (2017-12-11) 127 | - 新增: `pingpp.setUrlReturnCallback` 方法,支持获取支付页面地址而不是直接跳转 128 | 129 | ### 2.1.15 (2017-10-18) 130 | - 新增: 支付宝口碑渠道 (alipay_qr) 131 | - 修复:DOM security error 132 | 133 | ### 2.1.14 (2017-09-28) 134 | - 修复:账户系统 1.4 中多 charge 取值问题 135 | 136 | ### 2.1.13 (2017-08-28) 137 | - 更新:兼容账户系统 1.4 138 | 139 | ### 2.1.12 (2017-08-09) 140 | - 修复:测试模式下 wx_pub 中 notify url 报错问题 141 | 142 | ### 2.1.11 (2017-07-14) 143 | - 修复:修复alipay_pc_direct兼容性 144 | 145 | ### 2.1.10 (2017-07-11) 146 | - 新增:添加 招行一网通(cmb_wallet) 147 | 148 | ### 2.1.9 (2017-06-21) 149 | - 新增:添加 线下扫码渠道(isv_wap) 150 | - 更改:更新QQ钱包公众号 151 | - 更改: 兼容 alipay_pc_direct 2.0 152 | - 修复:localStorage为null报错 153 | 154 | ### 2.1.8 (2017-03-09) 155 | - 更改:兼容 IE10 浏览器 156 | 157 | ### 2.1.7 (2017-01-12) 158 | - 新增:添加 微信小程序 159 | - 更改:支付宝(alipay_wap)支持 openapi 160 | 161 | ### 2.1.6 (2017-01-04) 162 | - 修复:test 模式支付 order 成功 order 状态未更正问题 163 | - 新增:QQ 钱包公众号(qpay_pub) 164 | 165 | ### 2.1.5 166 | - 修复:Safari 无痕模式报错 167 | 168 | ### 2.1.4 169 | - require 源码,而不是构建完的 js 170 | 171 | ### 2.1.3 172 | - 新增:支持京东支付2.0 173 | - 修复:微信支付测试模式 174 | - 修复:Android WebView localStorage 兼容问题 175 | 176 | ### 2.1.2 177 | - 新增:支持 wx_wap 178 | 179 | ### 2.1.1 180 | - 更改:修复在 head 导入报错问题 181 | 182 | ### 2.1.0 183 | - 更改:支持模块化构建 184 | 185 | ### 2.0.8 186 | - 新增:添加企业网银支付(cp_b2b) 187 | 188 | ### 2.0.7 189 | - 更改:重命名文件名为 pingpp.js 190 | - 更改:删除无用配置 191 | - 新增:增加 PC 端接口,pingpp-pc.js,调用方式为 `pingppPc.createPayment(charge, callback)` 192 | 193 | ### 2.0.6 194 | - 新增:添加京东钱包 WAP 支付 195 | 196 | ### 2.0.5 197 | - 新增:添加易宝 WAP 支付 198 | 199 | ### 2.0.4 200 | - 更改:改进数组元素类型的判断 201 | 202 | ### 2.0.3 203 | - 新增:支持微信公众号 JS-SDK 调起支付 204 | - 新增:添加在微信中使用支付宝手机网页支付的处理 205 | 206 | ### 2.0.2 207 | - 更改:修正微信公众号 JSAPI 未加载完成时调用的问题 208 | 209 | ### 2.0.1 210 | - 更改:新的测试模式 211 | - 更改:合并 HTML5 和 微信公众号 SDK 212 | 213 | ### 2.0.0 214 | - 更改:添加新渠道:百付宝WAP 215 | - 更改:调用方法添加 callback,未跳转渠道前,出错时可返回错误信息 216 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Pingpp HTML5 SDK 2 | 3 | ## 目录 4 | 5 | * [1. 使用 Ping++ SDK 标准版](#1) 6 | * [1.1 支持的渠道](#1.1) 7 | * [1.2 如何构建](#1.2) 8 | * [1.3 使用说明](#1.3) 9 | * [1.4 接入注意事项](#1.4) 10 | 11 | * [2. 常见问题](#2) 12 | 13 | ##

使用 Ping++ SDK 标准版

14 | 15 | ###

支持的渠道

16 | 17 | - 手机网页支付 18 | 1. 支付宝手机网页支付(alipay_wap) 19 | 2. 百度钱包手机网页支付(bfb_wap) 20 | 3. 银联全渠道手机网页支付(upacp_wap) 21 | 4. 微信WAP支付(wx_wap) 22 | 5. 微信小程序支付(wx_lite) 23 | 6. 易宝手机网页支付(yeepay_wap) 24 | 7. 京东手机网页支付(jdpay_wap) 25 | 8. 招行一网通支付(cmb_wallet) 26 | 27 | - PC 网页支付 28 | 1. 支付宝电脑网站支付 (alipay_pc_direct) 29 | 2. 银联网关支付 (upacp_pc) 30 | 3. 银联企业网银支付 (cp_b2b) 31 | 4. 拉卡拉网联B2C/B2B支付 32 | 33 | - 微信公众账号支付(wx_pub) 34 | - QQ 公众号支付 (qpay_pub) 35 | - 支付宝口碑 (alipay_qr) 36 | - 线下扫码支付(isv_wap) 37 | - 线下小程序支付(isv_lite) 38 | 39 | ###

如何构建

40 | 41 | [dist](/dist) 目录下提供了已经构建好的 SDK,使用的命令是 `gulp build --alipay_in_weixin --agreement`。 42 | 43 | #### 全局安装 gulp 44 | 45 | ``` bash 46 | npm install -g gulp 47 | ``` 48 | 49 | #### 默认构建 50 | 51 | 默认会包含所有渠道 52 | 53 | ``` bash 54 | npm run build 55 | ``` 56 | 57 | #### 自定义构建 58 | 59 | 安装依赖 60 | 61 | ``` bash 62 | npm install 63 | ``` 64 | 65 | ##### --channels 66 | 67 | 选择渠道,渠道字段用空格或者英文逗号分割,例: 68 | 69 | ``` bash 70 | gulp build --channels="alipay_wap wx_pub upacp_wap" 71 | ``` 72 | 73 | 可选的渠道模块请查看 [src/channels](/src/channels) 目录下的文件名 74 | 75 | ###### wx_lite 76 | 77 | 因为微信小程序中 不能使用其他支付渠道,构建时请添加该参数 78 | 79 | ``` bash 80 | gulp build --channels=wx_lite 81 | ``` 82 | 83 | ##### --name 84 | 85 | 设置对象变量名 86 | 87 | ``` bash 88 | gulp build --name="pingppPc" --channels="alipay_pc_direct upacp_pc" 89 | ``` 90 | 91 | ##### --alipay_in_weixin 92 | 93 | 如果要在微信内使用支付宝手机网页支付,请添加该参数 94 | 95 | ``` bash 96 | gulp build --alipay_in_weixin 97 | ``` 98 | 99 | 同时,将 [`alipay_in_weixin`](/alipay_in_weixin) 目录下的 [pay.htm](/alipay_in_weixin/pay.htm) 放于你服务器可访问的路径下,如下两种方法: 100 | 101 | - 默认情况下,访问该文件的 URL 需要与你的支付页面时同级的。例: 102 | 支付页面 URL:http://localhost/project/payment?a=b&c=d 103 | 该文件 URL:http://localhost/project/pay.htm 104 | 105 | - 你也可以调用 `setAPURL` 方法来自定义该文件 URL。 106 | 107 | ``` js 108 | pingpp.setAPURL('http://localhost/your/custom/url'); 109 | ``` 110 | 111 | 该文件([pay.htm](/alipay_in_weixin/pay.htm))内的 `CURRENT_PAGE_URL` 变量也设置为相同的值。 112 | 113 | ##### --wx_jssdk 114 | 115 | 如果想使用微信的 JS-SDK 来调起支付,请添加该参数 116 | 117 | ``` bash 118 | gulp build --wx_jssdk 119 | ``` 120 | 121 | ##### --agreement 122 | 123 | 如果需要使用签约接口,请添加该参数 124 | 125 | ``` bash 126 | gulp build --agreement 127 | ``` 128 | 129 | ###

使用说明

130 | 131 | #### 引入 JS 文件 132 | 133 | - Browserify 打包方式 134 | 135 | 首先使用 `npm` 下载 136 | 137 | ``` bash 138 | npm install pingpp-js 139 | ``` 140 | 141 | 使用 142 | 143 | ``` javascript 144 | var pingpp = require('pingpp-js'); 145 | ``` 146 | 147 | - script 标签方式 148 | 149 | ``` html 150 | 151 | ``` 152 | 153 | #### 使用服务端创建的 [charge](https://www.pingxx.com/docs/overview) 调用接口 154 | 155 | ``` javascript 156 | // 参数一:支付参数 charge/order/recharge 157 | // 参数二:支付结果回调 158 | pingpp.createPayment(data, function(result, err) { 159 | if (result == "success") { 160 | // 只有微信公众账号 (wx_pub)、微信小程序 (wx_lite)、QQ 公众号 (qpay_pub)、支付宝口碑 (alipay_qr) 161 | // 支付成功的结果会在这里返回,其他的支付结果都会跳转到 extra 中对应的 URL。 162 | } else if (result == "fail") { 163 | // data 不正确或者微信公众账号/微信小程序/QQ 公众号/支付宝口碑支付失败时会在此处返回 164 | } else if (result == "cancel") { 165 | // 微信公众账号、微信小程序、QQ 公众号、支付宝口碑支付取消支付 166 | } 167 | }); 168 | ``` 169 | 170 | 如果 `charge` 正确的话,会跳转到相应的支付页面,要求用户进行付款。 171 | 172 | 用户支付成功后,会跳转到创建 `charge` 时定义的 `result_url` 或者 `success_url`。如果用户取消支付,则会跳转到 `result_url` 或者 `cancel_url`(具体情况根据渠道不同会有所变化)。 173 | 174 | #### 如果不想直接跳转到支付页面,而是获取支付页面地址 175 | 176 | 在调用 `pingpp.createPayment` 之前,调用 177 | 178 | ```javascript 179 | pingpp.setUrlReturnCallback(callback, channels); 180 | ``` 181 | 182 | ##### 参数 callback 183 | 184 | 回调函数 185 | 186 | - 第一个参数接受错误信息,没有错误时为 null。 187 | - 第二个参数为支付界面地址的值。 188 | 189 | ##### 参数 channels 190 | 191 | 需要启用该功能的渠道列表,类型为 `array`。默认值为 `['alipay_pc_direct']`。 192 | 193 | ##### 示例 194 | 195 | ```javascript 196 | pingpp.setUrlReturnCallback(function (err, url) { 197 | // 自行处理跳转或者另外打开支付页面地址(url) 198 | // window.location.href = url; 199 | }, ['alipay_pc_direct', 'alipay_wap']); 200 | ``` 201 | 202 | #### 调用签约接口 203 | 204 | 从服务端获取 `agreement` 对象之后,调用 205 | 206 | ```js 207 | pingpp.signAgreement(agreement, callback); 208 | ``` 209 | 210 | 签约接口在对象不正确等情况下,会回调 `callback`。 211 | 212 | 如果对象正确,则会跳转到相应的签约页面,签约结果**不会**在 `callback` 返回,需要商户自己调用服务端接口查询 `agreement` 状态。 213 | 214 | ###

微信公众号接入注意事项

215 | 216 | _以下示例中,Server-SDK 以 `php` 为例,其他语言请参考各语言 SDK 的文档或者示例_ 217 | 218 | #### 关于 open_id 219 | 220 | ##### 用 Server-SDK 取得 `open_id`(微信公众号授权用户唯一标识)。 221 | 222 | 先跳转到微信获取`授权 code`,地址由下方代码生成,`$wx_app_id` 是你的`微信公众号应用唯一标识`,`$redirect_url` 是用户确认授权后跳转的地址,用来接收 `code`。 223 | 224 | ```php 225 | 常见问题 327 | 328 | #### 问题一: H5 页面微信公众号支付调用 Ping++ 提示失败 (来源:工单) 329 | 330 | 返回结果: `get_brand_wcpay_request: fail` 331 | 332 | - 报错原因:微信授权目录填写错误。 333 | - 解决方案:详见[帮助中心](https://help.pingxx.com/article/123339) 334 | 335 | #### 问题二:微信内调用支付宝没出现引导界面,只有复制链接到浏览器 336 | 337 | - 报错原因:pay.htm 路径出错 338 | - 解决方案: 339 | 1. 默认情况下,访问该文件的 URL 需要与你的支付页面时同级的。例: 340 | 支付页面 URL:http://localhost/project/payment?a=b&c=d 341 | 该文件 URL:http://localhost/project/pay.htm 342 | 2. 你也可以调用 `setAPURL` 方法来自定义该文件 URL。 343 | 344 | ``` js 345 | pingpp.setAPURL('http://localhost/your/custom/url'); 346 | ``` 347 | 348 | 该文件([pay.htm](/alipay_in_weixin/pay.htm))内的 `CURRENT_PAGE_URL` 变量也设置为相同的值。 349 | 350 | #### 问题三:调不起支付,返回报错信息 json_decode_fail 351 | 352 | - 报错原因:传入的参数不是正确的 JSON 字符串或者 JSON 对象 353 | - 解决方案:客户端调用 SDK 时,确认服务端输出到客户端时,数据的正确性。 354 | -------------------------------------------------------------------------------- /alipay_in_weixin/pay.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 支付提示 6 | 7 | 8 | 9 | 10 | 11 | 112 | 113 | 114 |
115 |
116 | 请在菜单中选择在浏览器中打开,
117 | 以完成支付 118 |
119 |
120 |
121 | 122 | 125 | 152 | 153 | 154 | -------------------------------------------------------------------------------- /demo/img/bgpic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingPlusPlus/pingpp-js/fcbb68b567f4beb4b09c89282ab1d8375b16a4ee/demo/img/bgpic.jpg -------------------------------------------------------------------------------- /demo/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PingPlusPlus/pingpp-js/fcbb68b567f4beb4b09c89282ab1d8375b16a4ee/demo/img/logo.png -------------------------------------------------------------------------------- /demo/img/logo/logo-l.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | z 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /demo/styles/pinus.css: -------------------------------------------------------------------------------- 1 | * { 2 | -webkit-box-sizing: border-box; 3 | -moz-box-sizing: border-box; 4 | box-sizing: border-box; 5 | -webkit-box-shadow: none; 6 | box-shadow: none; 7 | border: none; 8 | } 9 | html, 10 | body { 11 | border: 0; 12 | font-family: "Helvetica-Neue", "Helvetica", Arial, sans-serif; 13 | line-height: 1.5; 14 | margin: 0; 15 | padding: 0; 16 | width: 100%; 17 | } 18 | div, 19 | span, 20 | object, 21 | iframe, 22 | img, 23 | table, 24 | caption, 25 | thead, 26 | tbody, 27 | tfoot, 28 | tr, 29 | tr, 30 | td, 31 | article, 32 | aside, 33 | canvas, 34 | details, 35 | figure, 36 | hgroup, 37 | menu, 38 | nav, 39 | footer, 40 | header, 41 | section, 42 | summary, 43 | mark, 44 | audio, 45 | video { 46 | border: 0; 47 | margin: 0; 48 | padding: 0; 49 | } 50 | h1, 51 | h2, 52 | h3, 53 | h4, 54 | h5, 55 | h6, 56 | p, 57 | blockquote, 58 | pre, 59 | a, 60 | abbr, 61 | address, 62 | cit, 63 | code, 64 | del, 65 | dfn, 66 | em, 67 | ins, 68 | q, 69 | samp, 70 | small, 71 | strong, 72 | sub, 73 | sup, 74 | b, 75 | i, 76 | hr, 77 | dl, 78 | dt, 79 | dd, 80 | ol, 81 | ul, 82 | li, 83 | fieldset, 84 | legend, 85 | label { 86 | border: 0; 87 | font-size: 100%; 88 | vertical-align: baseline; 89 | margin: 0; 90 | padding: 0; 91 | } 92 | article, 93 | aside, 94 | canvas, 95 | figure, 96 | figure img, 97 | figcaption, 98 | hgroup, 99 | footer, 100 | header, 101 | nav, 102 | section, 103 | audio, 104 | video { 105 | display: block; 106 | } 107 | table { 108 | border-collapse: separate; 109 | border-spacing: 0; 110 | } 111 | table caption, 112 | table th, 113 | table td { 114 | text-align: left; 115 | vertical-align: middle; 116 | } 117 | a img { 118 | border: 0; 119 | } 120 | :focus { 121 | outline: 0; 122 | } 123 | /* color */ 124 | /* spacing */ 125 | /* fontsize */ 126 | /* screen & popup & content */ 127 | /* btn */ 128 | * { 129 | color: #4d4d4d; 130 | font-size: 14px; 131 | font-weight: 100; 132 | -webkit-font-smoothing: antialiased; 133 | line-height: 2; 134 | font-family: AvenirNext-Regular, 'proxima-nova', 'Hiragino Sans GB', 'Microsoft YaHei', 'WenQuanYi Micro Hei', 'Open Sans', 'Helvetica Neue', Arial, sans-serif; 135 | } 136 | ::selection { 137 | color: #ffffff; 138 | text-shadow: none; 139 | background: #4d4d4d; 140 | } 141 | p { 142 | font-size: 14px; 143 | line-height: 2; 144 | padding: 7px 0; 145 | } 146 | a { 147 | color: #4d4d4d; 148 | text-decoration: none; 149 | -webkit-transition: all 0.2s ease-out; 150 | -moz-transition: all 0.2s ease-out; 151 | -ms-transition: all 0.2s ease-out; 152 | -o-transition: all 0.2s ease-out; 153 | } 154 | li { 155 | list-fx-position: outside; 156 | text-align: -webkit-match-parent; 157 | } 158 | code { 159 | font-family: monospace, menlo; 160 | } 161 | q { 162 | quotes: "「" "」"; 163 | } 164 | b { 165 | font-weight: bold; 166 | } 167 | h1 { 168 | font-size: 36px; 169 | } 170 | h2 { 171 | font-size: 24px; 172 | } 173 | h3 { 174 | font-size: 14px; 175 | font-weight: bold; 176 | } 177 | /* block > content */ 178 | .block { 179 | width: 100%; 180 | display: block; 181 | max-width: 1440px; 182 | margin: auto; 183 | padding: 84px 0; 184 | } 185 | @media (min-width: 480px) { 186 | .block { 187 | padding: 84px 0; 188 | } 189 | } 190 | @media (min-width: 768px) { 191 | .block { 192 | padding: 84px 0; 193 | } 194 | } 195 | @media (min-width: 1024px) { 196 | .block { 197 | padding: 84px 0; 198 | } 199 | } 200 | .popup .shadow { 201 | width: 100%; 202 | height: 100%; 203 | display: block; 204 | position: fixed; 205 | top: 0; 206 | background: rgba(0, 0, 0, 0.25); 207 | } 208 | .popup .paper { 209 | background: #ffffff; 210 | display: block; 211 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25); 212 | -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25); 213 | -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25); 214 | margin: 0 auto; 215 | padding: 28px 0; 216 | width: 100%; 217 | } 218 | @media (min-width: 480px) { 219 | .popup .paper { 220 | margin: 28px auto; 221 | padding: 28px 0; 222 | width: 424px; 223 | } 224 | } 225 | @media (min-width: 768px) { 226 | .popup .paper { 227 | margin: 56px auto; 228 | padding: 56px 0; 229 | width: 656px; 230 | } 231 | } 232 | @media (min-width: 1024px) { 233 | .popup .paper { 234 | margin: 56px auto; 235 | padding: 56px 0; 236 | width: 912px; 237 | } 238 | } 239 | .content { 240 | display: block; 241 | margin-left: auto; 242 | margin-right: auto; 243 | min-width: 768px; 244 | width: 304px; 245 | } 246 | @media (min-width: 480px) { 247 | .content { 248 | width: 368px; 249 | } 250 | } 251 | @media (min-width: 768px) { 252 | .content { 253 | width: 544px; 254 | } 255 | } 256 | @media (min-width: 1024px) { 257 | .content { 258 | width: 100%; 259 | max-width: 1440px; 260 | } 261 | } 262 | .grid-2 { 263 | overflow: hidden; 264 | } 265 | .grid-2 .cell { 266 | width: 100%; 267 | display: inline-block; 268 | float: left; 269 | } 270 | @media (min-width: 480px) { 271 | .grid-2 .cell { 272 | width: 100%; 273 | } 274 | } 275 | @media (min-width: 768px) { 276 | .grid-2 .cell { 277 | width: 244px; 278 | } 279 | .grid-2 .cell:first-child { 280 | margin-right: 56px; 281 | margin-bottom: 28px; 282 | } 283 | } 284 | @media (min-width: 1024px) { 285 | .grid-2 .cell { 286 | width: 372px; 287 | } 288 | .grid-2 .cell:first-child { 289 | margin-right: 56px; 290 | margin-bottom: 28px; 291 | } 292 | } 293 | header { 294 | width: 100%; 295 | background: #ffffff; 296 | position: fixed; 297 | } 298 | header .content { 299 | padding: 0; 300 | padding-top: 28px; 301 | min-width: 768px; 302 | } 303 | header .content a { 304 | line-height: 1; 305 | padding: 14px 0; 306 | } 307 | header .content a:active { 308 | background: #f7f7f7; 309 | } 310 | header .content .logo { 311 | height: 42px; 312 | } 313 | header .content .logo span { 314 | display: block; 315 | background: url(../img/logo/logo-l.svg) no-repeat top center; 316 | -webkit-background-size: cover; 317 | background-size: cover; 318 | width: 52px; 319 | height: 7px; 320 | } 321 | header .content .logo:active { 322 | background: none; 323 | opacity: 0.5; 324 | } 325 | header .space { 326 | display: block; 327 | height: 0; 328 | } 329 | @media (min-width: 768px) { 330 | header .content { 331 | overflow: hidden; 332 | } 333 | header .content .left { 334 | float: left; 335 | } 336 | header .content .right { 337 | float: right; 338 | } 339 | header .content a { 340 | display: inline-block; 341 | padding: 14px 14px; 342 | } 343 | header .content .logo { 344 | padding-left: 0; 345 | padding-right: 0; 346 | } 347 | } 348 | footer { 349 | background: #ffffff; 350 | } 351 | footer .content { 352 | padding: 0 !important; 353 | padding-bottom: 14px !important; 354 | } 355 | footer .content a { 356 | display: block; 357 | line-height: 1; 358 | padding: 14px 0; 359 | } 360 | footer .content a:active { 361 | background: #f7f7f7; 362 | } 363 | footer .content .copyright { 364 | padding: 0; 365 | display: block; 366 | padding-top: 14px; 367 | } 368 | footer .content .endorse { 369 | font-size: 10px; 370 | padding: 0; 371 | padding-bottom: 7px; 372 | line-height: 2; 373 | color: #999999; 374 | } 375 | @media (min-width: 768px) { 376 | footer .content { 377 | overflow: hidden; 378 | } 379 | footer .content a { 380 | display: inline-block; 381 | padding: 14px 14px; 382 | } 383 | footer .content .left { 384 | float: left; 385 | } 386 | footer .content .right { 387 | float: right; 388 | } 389 | } 390 | form label { 391 | display: block; 392 | width: 100%; 393 | } 394 | form .options input[type="radio"] { 395 | display: none; 396 | } 397 | form .options label { 398 | width: auto; 399 | display: inline-block; 400 | margin-right: 14px; 401 | } 402 | form .options label:before { 403 | content: ""; 404 | display: inline-block; 405 | position: relative; 406 | top: 2px; 407 | width: 14px; 408 | height: 14px; 409 | margin-right: 7px; 410 | border-radius: 14px; 411 | -webkit-border-radius: 14px; 412 | -moz-border-radius: 14px; 413 | -moz-box-shadow: inset 0px 0px 0px 1px #b3b3b3; 414 | -webkit-box-shadow: inset 0px 0px 0px 1px #b3b3b3; 415 | box-shadow: inset 0px 0px 0px 1px #b3b3b3; 416 | background: rgba(0, 0, 0, 0); 417 | -webkit-transition: all 0.2s ease-out; 418 | -moz-transition: all 0.2s ease-out; 419 | -ms-transition: all 0.2s ease-out; 420 | -o-transition: all 0.2s ease-out; 421 | } 422 | form .options label:hover { 423 | cursor: pointer; 424 | } 425 | form .options label:active:before { 426 | -moz-box-shadow: inset 0px 0px 0px 1px #4d4d4d; 427 | -webkit-box-shadow: inset 0px 0px 0px 1px #4d4d4d; 428 | box-shadow: inset 0px 0px 0px 1px #4d4d4d; 429 | } 430 | form .options input[type="radio"]:checked + label:before { 431 | -moz-box-shadow: inset 0px 0px 0px 1px #4d4d4d; 432 | -webkit-box-shadow: inset 0px 0px 0px 1px #4d4d4d; 433 | box-shadow: inset 0px 0px 0px 1px #4d4d4d; 434 | background: #4d4d4d; 435 | } 436 | form input[type='text'] { 437 | -webkit-transition: all 0.2s ease-out; 438 | -moz-transition: all 0.2s ease-out; 439 | -ms-transition: all 0.2s ease-out; 440 | -o-transition: all 0.2s ease-out; 441 | -moz-box-shadow: inset 0px -1px 0px 0px #b3b3b3; 442 | -webkit-box-shadow: inset 0px -1px 0px 0px #b3b3b3; 443 | box-shadow: inset 0px -1px 0px 0px #b3b3b3; 444 | font-weight: normal; 445 | background: none; 446 | } 447 | form input[type='text']:focus { 448 | -moz-box-shadow: inset 0px -1px 0px 0px #4d4d4d; 449 | -webkit-box-shadow: inset 0px -1px 0px 0px #4d4d4d; 450 | box-shadow: inset 0px -1px 0px 0px #4d4d4d; 451 | } 452 | form textarea { 453 | -webkit-transition: all 0.2s ease-out; 454 | -moz-transition: all 0.2s ease-out; 455 | -ms-transition: all 0.2s ease-out; 456 | -o-transition: all 0.2s ease-out; 457 | -moz-box-shadow: inset 0px -1px 0px 0px #b3b3b3; 458 | -webkit-box-shadow: inset 0px -1px 0px 0px #b3b3b3; 459 | box-shadow: inset 0px -1px 0px 0px #b3b3b3; 460 | display: block; 461 | min-width: 100%; 462 | min-height: 100px; 463 | resize: vertical; 464 | font-weight: normal; 465 | background: none; 466 | } 467 | form textarea:focus { 468 | -moz-box-shadow: inset 0px -1px 0px 0px #4d4d4d; 469 | -webkit-box-shadow: inset 0px -1px 0px 0px #4d4d4d; 470 | box-shadow: inset 0px -1px 0px 0px #4d4d4d; 471 | } 472 | form .btn-bar button { 473 | -webkit-transition: all 0.2s ease-out; 474 | -moz-transition: all 0.2s ease-out; 475 | -ms-transition: all 0.2s ease-out; 476 | -o-transition: all 0.2s ease-out; 477 | background: none; 478 | font-weight: normal; 479 | padding: 7px 14px; 480 | display: inline-block; 481 | margin-bottom: 7px; 482 | width: 100%; 483 | min-width: 120px; 484 | display: block; 485 | } 486 | form .btn-bar button:hover { 487 | cursor: pointer; 488 | } 489 | form .btn-bar button:active { 490 | background: #f7f7f7; 491 | } 492 | @media (min-width: 480px) { 493 | form .btn-bar button { 494 | margin-right: 7px; 495 | width: auto; 496 | display: inline-block; 497 | } 498 | form .btn-bar button:last-child { 499 | margin-right: auto; 500 | } 501 | } 502 | form .btn-bar .btn-main { 503 | -webkit-transition: all 0.2s ease-out; 504 | -moz-transition: all 0.2s ease-out; 505 | -ms-transition: all 0.2s ease-out; 506 | -o-transition: all 0.2s ease-out; 507 | background: none; 508 | font-weight: normal; 509 | padding: 7px 14px; 510 | background: #4d4d4d; 511 | color: #ffffff; 512 | } 513 | form .btn-bar .btn-main:hover { 514 | cursor: pointer; 515 | } 516 | form .btn-bar .btn-main:active { 517 | background: #000000; 518 | } 519 | article h1 { 520 | margin-top: 28px; 521 | } 522 | article h2 { 523 | margin-top: 28px; 524 | } 525 | article h3 { 526 | margin-top: 28px; 527 | margin-bottom: 14px; 528 | } 529 | article code { 530 | background: #f7f7f7; 531 | border-radius: 3px; 532 | -webkit-border-radius: 3px; 533 | -moz-border-radius: 3px; 534 | } 535 | article pre { 536 | min-width: 100%; 537 | padding: 14px; 538 | background: #f7f7f7; 539 | } 540 | article table { 541 | min-width: 100%; 542 | } 543 | article a { 544 | -moz-box-shadow: inset 0px -1px 0px 0px #b3b3b3; 545 | -webkit-box-shadow: inset 0px -1px 0px 0px #b3b3b3; 546 | box-shadow: inset 0px -1px 0px 0px #b3b3b3; 547 | } 548 | article a:active { 549 | -moz-box-shadow: inset 0px -1px 0px 0px #4d4d4d; 550 | -webkit-box-shadow: inset 0px -1px 0px 0px #4d4d4d; 551 | box-shadow: inset 0px -1px 0px 0px #4d4d4d; 552 | } 553 | /* 554 | 555 | app.less 556 | 557 | 558 | */ 559 | footer { 560 | position: relative; 561 | background: none; 562 | } 563 | header { 564 | position: relative; 565 | } 566 | header .h_content { 567 | width: 100%; 568 | height: auto; 569 | padding-top: 28px; 570 | } 571 | header .h_content span { 572 | width: 86px; 573 | height: 14px; 574 | margin: auto; 575 | display: block; 576 | background: url(../img/logo.png) no-repeat top center; 577 | margin-bottom: 14px; 578 | } 579 | body { 580 | height: 100%; 581 | overflow-x: hidden; 582 | } 583 | footer { 584 | position: relative; 585 | bottom: 0; 586 | } 587 | footer .content2 { 588 | padding: 0 !important; 589 | padding-bottom: 14px !important; 590 | } 591 | footer .content2 a { 592 | display: block; 593 | line-height: 1; 594 | padding: 14px; 595 | } 596 | footer .content2 a:active { 597 | background: #f7f7f7; 598 | } 599 | footer .content2 .copyright { 600 | padding: 0; 601 | display: block; 602 | padding-left: 14px; 603 | } 604 | footer .content2 .endorse { 605 | font-size: 10px; 606 | padding: 0; 607 | padding-left: 14px; 608 | line-height: 2; 609 | color: #999999; 610 | } 611 | @media (max-width: 640px) { 612 | footer { 613 | position: relative; 614 | } 615 | } 616 | .content2 { 617 | display: block; 618 | margin-left: auto; 619 | margin-right: auto; 620 | width: 100%; 621 | } 622 | .block { 623 | padding: 0; 624 | overflow: hidden; 625 | } 626 | .app { 627 | width: 100%; 628 | margin: auto; 629 | padding: 14px; 630 | height: auto; 631 | overflow: hidden; 632 | } 633 | .iphone { 634 | -webkit-transition: all 0.2s ease-out; 635 | -moz-transition: all 0.2s ease-out; 636 | -ms-transition: all 0.2s ease-out; 637 | -o-transition: all 0.2s ease-out; 638 | width: 100%; 639 | max-width: 960px; 640 | height: auto; 641 | margin: 0 auto; 642 | display: block; 643 | margin-bottom: 28px; 644 | -webkit-background-size: cover; 645 | background-size: cover; 646 | } 647 | @media only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (min--moz-device-pixel-ratio: 1.5), only screen and (-o-min-device-pixel-ratio: 3/2), only screen and (min-device-pixel-ratio: 1.5) { 648 | background: url(../img/bgpic.jpg) no-repeat center; 649 | } 650 | .text_amount { 651 | display: block; 652 | margin: 56px auto; 653 | width: 300px; 654 | } 655 | .text_amount input[type="text"] { 656 | width: 100%; 657 | -webkit-transition: all 0.2s ease-out; 658 | -moz-transition: all 0.2s ease-out; 659 | -ms-transition: all 0.2s ease-out; 660 | -o-transition: all 0.2s ease-out; 661 | border-bottom: 1px solid #999999; 662 | } 663 | .text_amount input[type="text"]:focus { 664 | -webkit-transition: all 0.2s ease-out; 665 | -moz-transition: all 0.2s ease-out; 666 | -ms-transition: all 0.2s ease-out; 667 | -o-transition: all 0.2s ease-out; 668 | border-bottom: 1px solid #000000; 669 | } 670 | .ch { 671 | width: 50%; 672 | margin: auto; 673 | height: auto; 674 | overflow: hidden; 675 | font-size: 16px; 676 | } 677 | .ch .wx { 678 | width: 20%; 679 | min-width: 69px; 680 | padding: 14px; 681 | margin-right: 20%; 682 | cursor: pointer; 683 | display: block; 684 | float: left; 685 | text-align: center; 686 | font-size: 16px; 687 | display: none; 688 | -webkit-transition: all 0.2s ease-out; 689 | -moz-transition: all 0.2s ease-out; 690 | -ms-transition: all 0.2s ease-out; 691 | -o-transition: all 0.2s ease-out; 692 | } 693 | .ch .wx:hover { 694 | -webkit-transition: all 0.2s ease-out; 695 | -moz-transition: all 0.2s ease-out; 696 | -ms-transition: all 0.2s ease-out; 697 | -o-transition: all 0.2s ease-out; 698 | color: #000000; 699 | } 700 | .ch .wx:active { 701 | -webkit-transition: all 0.2s ease-out; 702 | -moz-transition: all 0.2s ease-out; 703 | -ms-transition: all 0.2s ease-out; 704 | -o-transition: all 0.2s ease-out; 705 | background-color: #f7f7f7; 706 | } 707 | @media (max-width: 960px) { 708 | .ch .wx { 709 | margin-right: 10%; 710 | } 711 | } 712 | .ch .up { 713 | width: 20%; 714 | min-width: 91px; 715 | padding: 14px; 716 | cursor: pointer; 717 | margin-right: 20%; 718 | display: block; 719 | float: left; 720 | text-align: center; 721 | font-size: 16px; 722 | -webkit-transition: all 0.2s ease-out; 723 | -moz-transition: all 0.2s ease-out; 724 | -ms-transition: all 0.2s ease-out; 725 | -o-transition: all 0.2s ease-out; 726 | } 727 | .ch .up:hover { 728 | -webkit-transition: all 0.2s ease-out; 729 | -moz-transition: all 0.2s ease-out; 730 | -ms-transition: all 0.2s ease-out; 731 | -o-transition: all 0.2s ease-out; 732 | color: #000000; 733 | } 734 | .ch .up:active { 735 | -webkit-transition: all 0.2s ease-out; 736 | -moz-transition: all 0.2s ease-out; 737 | -ms-transition: all 0.2s ease-out; 738 | -o-transition: all 0.2s ease-out; 739 | background-color: #f7f7f7; 740 | } 741 | @media (max-width: 960px) { 742 | .ch .up { 743 | margin-right: 10%; 744 | } 745 | } 746 | .ch .ap { 747 | width: 20%; 748 | min-width: 83px; 749 | padding: 14px; 750 | cursor: pointer; 751 | display: block; 752 | float: left; 753 | text-align: center; 754 | font-size: 16px; 755 | -webkit-transition: all 0.2s ease-out; 756 | -moz-transition: all 0.2s ease-out; 757 | -ms-transition: all 0.2s ease-out; 758 | -o-transition: all 0.2s ease-out; 759 | } 760 | .ch .ap:hover { 761 | -webkit-transition: all 0.2s ease-out; 762 | -moz-transition: all 0.2s ease-out; 763 | -ms-transition: all 0.2s ease-out; 764 | -o-transition: all 0.2s ease-out; 765 | color: #000000; 766 | } 767 | .ch .ap:active { 768 | -webkit-transition: all 0.2s ease-out; 769 | -moz-transition: all 0.2s ease-out; 770 | -ms-transition: all 0.2s ease-out; 771 | -o-transition: all 0.2s ease-out; 772 | background-color: #f7f7f7; 773 | } 774 | .tips { 775 | display: inline-block; 776 | width: 100%; 777 | padding: 14px; 778 | background: #4d4d4d; 779 | color: #fff; 780 | text-align: center; 781 | font-size: 16px; 782 | position: fixed; 783 | bottom: 0; 784 | z-index: 1000; 785 | } 786 | @media (max-width: 640px) { 787 | .iphone { 788 | -webkit-transition: all 0.2s ease-out; 789 | -moz-transition: all 0.2s ease-out; 790 | -ms-transition: all 0.2s ease-out; 791 | -o-transition: all 0.2s ease-out; 792 | margin: 0 auto; 793 | } 794 | .text_amount { 795 | display: block; 796 | margin: 28px auto; 797 | width: 100%; 798 | } 799 | .ch { 800 | width: 100%; 801 | } 802 | .ch .up, 803 | .ch .ap { 804 | -webkit-transition: all 0.2s ease-out; 805 | -moz-transition: all 0.2s ease-out; 806 | -ms-transition: all 0.2s ease-out; 807 | -o-transition: all 0.2s ease-out; 808 | float: none; 809 | display: inline-block; 810 | width: 100%; 811 | margin-bottom: 28px; 812 | border: 1px solid #f7f7f7; 813 | padding: 14px; 814 | } 815 | .ch .up:active, 816 | .ch .ap:active { 817 | -webkit-transition: all 0.2s ease-out; 818 | -moz-transition: all 0.2s ease-out; 819 | -ms-transition: all 0.2s ease-out; 820 | -o-transition: all 0.2s ease-out; 821 | border: 1px solid #999999; 822 | background-color: #f7f7f7; 823 | } 824 | } 825 | -------------------------------------------------------------------------------- /demo/views/wap.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | MiniCheckout 8 | 9 | 10 | 11 |
12 |
13 | 14 |
15 |
16 |
17 |
18 |
19 | 20 | 23 | 24 |
25 | 银联 WAP 26 | 支付宝 WAP 27 | 百度钱包 WAP 28 | 京东支付 WAP 29 | 易宝支付 WAP 30 | 微信浏览器内公众号支付 WAP 31 | 32 |
33 |
34 |
35 |
36 | 37 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /dist/pingpp.js: -------------------------------------------------------------------------------- 1 | !function(e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).pingpp=e()}(function(){return function r(t,l,i){function c(a,e){if(!l[a]){if(!t[a]){var n="function"==typeof require&&require;if(!e&&n)return n(a,!0);if(o)return o(a,!0);throw(e=new Error("Cannot find module '"+a+"'")).code="MODULE_NOT_FOUND",e}n=l[a]={exports:{}},t[a][0].call(n.exports,function(e){return c(t[a][1][e]||e)},n,n.exports,r,t,l,i)}return l[a].exports}for(var o="function"==typeof require&&require,e=0;e>16,n>>8&255,255&n));switch(a){case 1:n=r(e,i)<<18|r(e,i+1)<<12|r(e,i+2)<<6,l.push(String.fromCharCode(n>>16,n>>8&255));break;case 2:n=r(e,i)<<18|r(e,i+1)<<12,l.push(String.fromCharCode(n>>16))}return l.join('')},getbyte:function(e,a){e=e.charCodeAt(a);if(255>18)),i.push(t.charAt(n>>12&63)),i.push(t.charAt(n>>6&63)),i.push(t.charAt(63&n));switch(e.length-c){case 1:n=l(e,a)<<16,i.push(t.charAt(n>>18)+t.charAt(n>>12&63)+r+r);break;case 2:n=l(e,a)<<16|l(e,a+1)<<8,i.push(t.charAt(n>>18)+t.charAt(n>>12&63)+t.charAt(n>>6&63)+r)}return i.join('')}},r={url:'pay.htm',pay:function(e){e=encodeURIComponent(o.encode(e));l.call(t,'APURL')&&(r.url=t.APURL),location.href=r.url+'?goto='+e},decode:function(e){return o.decode(decodeURIComponent(e))}},a.exports=r},{"../../stash":54}],28:[function(e,a,n){e=e('./wx_lite');a.exports=e},{"./wx_lite":39}],29:[function(e,a,n){var r=e('./commons/redirect_base'),t=e('../callbacks'),l=e('../utils'),i={}.hasOwnProperty;a.exports={handleCharge:function(e){var a=e.extra;i.call(a,'pay_channel')?'wx'!==(a=a.pay_channel)||l.inWeixin()?'alipay'!==a||l.inAlipay()?r.handleCharge(e):t.innerCallback('fail',t.error('Not in the Alipay browser')):t.innerCallback('fail',t.error('Not in the WeChat browser')):t.innerCallback('fail',t.error('invalid_charge','charge 格式不正确'))}}},{"../callbacks":2,"../utils":58,"./commons/redirect_base":25}],30:[function(e,a,n){var r=e('../utils'),t={}.hasOwnProperty;a.exports={JDPAY_WAP_URL_OLD:'https://m.jdpay.com/wepay/web/pay',JDPAY_H5_URL:'https://h5pay.jd.com/jdpay/saveOrder',JDPAY_PC_URL:'https://wepay.jd.com/jdpay/saveOrder',handleCharge:function(e){var e=e.credential[e.channel],a=this.JDPAY_H5_URL;t.call(e,'channelUrl')?(a=e.channelUrl,delete e.channelUrl):t.call(e,'merchantRemark')&&(a=this.JDPAY_WAP_URL_OLD),r.formSubmit(a,'post',e)}}},{"../utils":58}],31:[function(e,a,n){var r=e('../utils'),t={}.hasOwnProperty;a.exports={handleCharge:function(e){e=e.credential[e.channel];t.call(e,'bankUrl')&&(request_url=e.bankUrl,delete e.bankUrl),r.formSubmit(request_url,'post',e)}}},{"../utils":58}],32:[function(e,a,n){arguments[4][31][0].apply(n,arguments)},{"../utils":58,dup:31}],33:[function(e,a,n){var r=e('../utils');a.exports={handleCharge:function(e){var e=e.credential[e.channel],a=e.channelUrl;delete e.channelUrl,r.formSubmit(a,'post',e)}}},{"../utils":58}],34:[function(e,a,n){var r=e('../utils');a.exports={handleCharge:function(e){var a=e.credential[e.channel];r.redirectTo(a,e.channel)}}},{"../utils":58}],35:[function(e,a,n){var r=e('../callbacks'),t=e('../utils'),l=e('../stash'),i={}.hasOwnProperty;a.exports={SRC_URL:'https://open.mobile.qq.com/sdk/qqapi.js?_bid=152',ID:'mqq_api',handleCharge:function(e){e=e.credential[e.channel];i.call(e,'token_id')?(l.tokenId=e.token_id,t.loadUrlJs(this.ID,this.SRC_URL,this.callpay)):r.innerCallback('fail',r.error('invalid_credential','missing_token_id'))},callpay:function(){if('undefined'!=typeof mqq){if(0==mqq.QQVersion)return r.innerCallback('fail',r.error('Not in the QQ client')),void delete l.tokenId;mqq.tenpay.pay({tokenId:l.tokenId},function(e){0==e.resultCode?r.innerCallback('success'):r.innerCallback('fail',r.error(e.retmsg))})}else r.innerCallback('fail',r.error('network_err'));delete l.tokenId}}},{"../callbacks":2,"../stash":54,"../utils":58}],36:[function(e,a,n){arguments[4][23][0].apply(n,arguments)},{"../utils":58,dup:23}],37:[function(e,a,n){var r=e('../utils');a.exports={UPACP_PC_URL:'https://gateway.95516.com/gateway/api/frontTransReq.do',handleCharge:function(e){e=e.credential[e.channel];r.formSubmit(this.UPACP_PC_URL,'post',e)}}},{"../utils":58}],38:[function(e,a,n){var r=e('../utils');a.exports={UPACP_WAP_URL:'https://gateway.95516.com/gateway/api/frontTransReq.do',handleCharge:function(e){e=e.credential[e.channel];r.formSubmit(this.UPACP_WAP_URL,'post',e)}}},{"../utils":58}],39:[function(e,a,n){var t=e('../stash'),l=e('../callbacks'),i={}.hasOwnProperty;a.exports={PINGPP_NOTIFY_URL_BASE:'https://notify.pingxx.com/notify',handleCharge:function(e){for(var a=e.credential[e.channel],n=['appId','timeStamp','nonceStr','package','signType','paySign'],r=0;r 0) { 39 | releaseObjectName = cmdOptions.name; 40 | } 41 | 42 | var b = browserify({ 43 | entries: entries, 44 | standalone: releaseObjectName, 45 | debug: true 46 | }); 47 | 48 | b.bundle() 49 | .pipe(source(destJsFile)) 50 | .pipe(buffer()) 51 | // .pipe(sourcemaps.init({ 52 | // loadMaps: true 53 | // })) 54 | .pipe(uglify({ 55 | mangle: { 56 | reserved: ['PingppSDK'] 57 | }, 58 | output: { 59 | quote_style: 3, 60 | max_line_len: 32000 61 | } 62 | })) 63 | .on('error', gutil.log) 64 | // .pipe(sourcemaps.write('./')) 65 | .pipe(dest(distDir)); 66 | 67 | cb(); 68 | } 69 | 70 | function modules(cb) { 71 | var channels = makeChannelModulesContent(); 72 | 73 | var libs = makeLibModulesContent(); 74 | var tmpl = fs.readFileSync(__dirname + '/mods.js.tmpl', 'utf8'); 75 | 76 | var modsContents = _.replace(tmpl, 77 | replaceChannelsPattern, 78 | channels.replacement); 79 | modsContents = _.replace(modsContents, 80 | replaceLibsPattern, 81 | libs.replacement); 82 | fs.writeFileSync(modsJsFile, modsContents, 'utf8'); 83 | console.log('Enabled channels: ' + channels.enabledChannels); 84 | cb(); 85 | } 86 | 87 | function clean(cb) { 88 | var paths = del.sync(distFiles); 89 | console.log('Deleted files and folders:\n' + paths.join('\n')); 90 | cb(); 91 | } 92 | 93 | var makeChannelModulesContent = function() { 94 | var channelPool = fs.readdirSync(channelsDirPath, 'utf8'); 95 | var allChannels = _.map(channelPool, function(ch) { 96 | if (ch.substr(0, 1) == '.' || ch.substr(-3) != '.js') { 97 | return undefined; 98 | } 99 | return ch.substr(0, ch.length - 3); 100 | }); 101 | allChannels = _.remove(allChannels, function(ch) { 102 | return typeof ch != 'undefined'; 103 | }); 104 | var enabledChannels; 105 | if (hasOwn.call(cmdOptions, 'channels') && cmdOptions.channels.length > 0) { 106 | enabledChannels = _.split(cmdOptions.channels, /[\s,]+/); 107 | _.forEach(enabledChannels, function(ch) { 108 | if (!_.includes(allChannels, ch)) { 109 | console.log('Channel ' + ch + 110 | ' is invalid. The channels you can use: ' + allChannels + '.'); 111 | process.exit(0); 112 | } 113 | }); 114 | } else { 115 | enabledChannels = _.remove(allChannels, function(ch) { 116 | return !_.includes(deprecatedChannels, ch); 117 | }); 118 | } 119 | 120 | var channelsContents = []; 121 | for (var i = 0; i < enabledChannels.length; i++) { 122 | var line = enabledChannels[i] + 123 | ': require(\'' + channelsDir + enabledChannels[i] + '\')'; 124 | channelsContents.push(line); 125 | } 126 | 127 | return { 128 | replacement: modnames2text(enabledChannels, channelsDir), 129 | enabledChannels: enabledChannels 130 | }; 131 | }; 132 | 133 | var makeLibModulesContent = function() { 134 | var extraBaseDir = './channels/extras/'; 135 | var extranames = []; 136 | if (hasOwn.call(cmdOptions, 'alipay_in_weixin') && 137 | cmdOptions.alipay_in_weixin) { 138 | extranames.push('ap'); 139 | } 140 | if (hasOwn.call(cmdOptions, 'wx_jssdk') && 141 | cmdOptions.wx_jssdk) { 142 | extranames.push('wx_jssdk'); 143 | } 144 | if (hasOwn.call(cmdOptions, 'agreement')) { 145 | extranames.push(['agreement', './agreement']); 146 | } 147 | 148 | return { 149 | replacement: modnames2text(extranames, extraBaseDir) 150 | }; 151 | }; 152 | 153 | var modnames2text = function(modnames, baseDir) { 154 | if (modnames.length === 0) { 155 | return ''; 156 | } 157 | if (baseDir === undefined || baseDir === null) { 158 | baseDir = ''; 159 | } 160 | var modsContents = []; 161 | for (var i = 0; i < modnames.length; i++) { 162 | var modname; 163 | var path; 164 | if (typeof modnames[i] === 'string') { 165 | modname = modnames[i]; 166 | path = baseDir + modname; 167 | } else if (Array.isArray(modnames[i])) { 168 | modname = modnames[i][0]; 169 | path = modnames[i][1]; 170 | } 171 | var line = modname + 172 | ': require(\'' + path + '\')'; 173 | modsContents.push(line); 174 | } 175 | 176 | return ' ' + _.join(modsContents, ',\n '); 177 | }; 178 | 179 | function watchFiles(cb) { 180 | var watcher = watch(scriptSrcFiles, series(build)); 181 | watcher.on('change', function(path) { 182 | console.log('File \'' + path + '\' was changed, running tasks...'); 183 | }); 184 | 185 | cb(); 186 | } 187 | 188 | exports.test = function(cb) { 189 | var test = require('./test/test.js'); 190 | test.run(); 191 | cb(); 192 | }; 193 | 194 | exports.build = series(clean, modules, build); 195 | exports.watch = series(clean, build, watchFiles); 196 | exports.default = series(build); 197 | -------------------------------------------------------------------------------- /mods.js.tmpl: -------------------------------------------------------------------------------- 1 | var hasOwn = {}.hasOwnProperty; 2 | var mods = {}; 3 | module.exports = mods; 4 | 5 | mods.channels = { 6 | <> 7 | }; 8 | 9 | mods.extras = { 10 | <> 11 | }; 12 | 13 | mods.transferChannels = { 14 | wx_pub: require('./transfer_channels/wx_pub'), 15 | } 16 | 17 | mods.getChannelModule = function(channel) { 18 | if (hasOwn.call(mods.channels, channel)) { 19 | return mods.channels[channel]; 20 | } 21 | return undefined; 22 | }; 23 | 24 | mods.getTransferChannelModule = function(channel) { 25 | if (hasOwn.call(mods.transferChannels, channel)) { 26 | return mods.transferChannels[channel]; 27 | } 28 | return undefined; 29 | }; 30 | 31 | mods.getExtraModule = function(name) { 32 | if (hasOwn.call(mods.extras, name)) { 33 | return mods.extras[name]; 34 | } 35 | return undefined; 36 | }; 37 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pingpp-js", 3 | "version": "2.4.0", 4 | "description": "Ping++ HTML5 SDK", 5 | "main": "src/main.js", 6 | "homepage": "https://www.pingxx.com", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/PingPlusPlus/pingpp-js.git" 10 | }, 11 | "keywords": [ 12 | "javascript", 13 | "payment", 14 | "library", 15 | "browser" 16 | ], 17 | "dependencies": {}, 18 | "devDependencies": { 19 | "browserify": "^16.2.3", 20 | "del": "^3.0.0", 21 | "gulp": "^4.0.0", 22 | "gulp-sourcemaps": "^2.6.4", 23 | "gulp-uglify": "^3.0.1", 24 | "gulp-util": "^3.0.8", 25 | "lodash": "^4.17.11", 26 | "minimist": "1.2.6", 27 | "vinyl-buffer": "^1.0.1", 28 | "vinyl-source-stream": "^2.0.0" 29 | }, 30 | "scripts": { 31 | "build": "npm install && gulp", 32 | "build:modules": "npm install && gulp build --alipay_in_weixin", 33 | "test": "gulp && gulp test" 34 | }, 35 | "author": "Afon", 36 | "license": "MIT" 37 | } 38 | -------------------------------------------------------------------------------- /src/agreement.js: -------------------------------------------------------------------------------- 1 | var hasOwn = {}.hasOwnProperty; 2 | var callbacks = require('./callbacks'); 3 | var utils = require('./utils'); 4 | 5 | module.exports = { 6 | signAgreement: function (agreement) { 7 | var agreementObj; 8 | if (typeof agreement === 'string') { 9 | try { 10 | agreementObj = JSON.parse(agreement); 11 | } catch (err) { 12 | callbacks.innerAgreementCallback('fail', 13 | callbacks.error('json_decode_fail', err)); 14 | return false; 15 | } 16 | } else { 17 | agreementObj = agreement; 18 | } 19 | 20 | if (typeof agreementObj === 'undefined') { 21 | callbacks.innerAgreementCallback('fail', 22 | callbacks.error('json_decode_fail')); 23 | return false; 24 | } 25 | 26 | if (!hasOwn.call(agreementObj, 'object') 27 | || agreementObj.object !== 'agreement' 28 | || !hasOwn.call(agreementObj, 'channel') 29 | || !hasOwn.call(agreementObj, 'credential') 30 | || typeof agreementObj.credential !== 'object' 31 | ) { 32 | callbacks.innerAgreementCallback('fail', 33 | callbacks.error('invalid_object')); 34 | return false; 35 | } 36 | 37 | if (!hasOwn.call(agreementObj.credential, agreementObj.channel)) { 38 | callbacks.innerAgreementCallback('fail', 39 | callbacks.error('invalid_credential')); 40 | return false; 41 | } 42 | 43 | var credential = agreementObj.credential[agreementObj.channel]; 44 | var urlToOpen; 45 | if (typeof credential === 'string') { 46 | urlToOpen = credential; 47 | } else if (hasOwn.call(credential, 'credential') 48 | && typeof credential.credential === 'string' 49 | ) { 50 | urlToOpen = credential.credential; 51 | } 52 | 53 | if (typeof urlToOpen === 'undefined') { 54 | callbacks.innerAgreementCallback('fail', 55 | callbacks.error('invalid_credential')); 56 | return false; 57 | } 58 | 59 | if (agreementObj.channel.substring(0, 6) === 'alipay' && utils.deviceDetectorMobile()) { 60 | var url = new URL(urlToOpen); 61 | urlToOpen = 'https://d.alipay.com/i/index.htm?iframeSrc=' 62 | + encodeURIComponent('alipays://platformapi/startapp?appId=60000157' 63 | +'&appClearTop=false&startMultApp=YES&sign_params=' 64 | + encodeURIComponent(url.search.substring(1)) 65 | ); 66 | } 67 | 68 | setTimeout(function() { 69 | utils.redirectTo(urlToOpen); 70 | }, 0); 71 | 72 | return true; 73 | } 74 | }; 75 | -------------------------------------------------------------------------------- /src/callbacks.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | userCallback: undefined, 3 | 4 | urlReturnCallback: undefined, 5 | 6 | urlReturnChannels: [ 7 | 'alipay_pc_direct', // 默认只开启 alipay_pc_direct 使用 callback 返回 URL 8 | ], 9 | 10 | userAgreementCallback: undefined, 11 | 12 | userTransferCallback: undefined, 13 | 14 | innerCallback: function (result, err) { 15 | if (typeof this.userCallback === 'function') { 16 | if (typeof err === 'undefined') { 17 | err = this.error(); 18 | } 19 | this.userCallback(result, err); 20 | this.userCallback = undefined; 21 | var payment_elements = require('./payment_elements'); 22 | payment_elements.clear(); 23 | } 24 | }, 25 | 26 | error: function (msg, extra) { 27 | msg = typeof msg === 'undefined' ? '' : msg; 28 | extra = typeof extra === 'undefined' ? '' : extra; 29 | return { 30 | msg: msg, 31 | extra: extra, 32 | }; 33 | }, 34 | 35 | triggerUrlReturnCallback: function (err, url) { 36 | if (typeof this.urlReturnCallback === 'function') { 37 | this.urlReturnCallback(err, url); 38 | } 39 | }, 40 | 41 | shouldReturnUrlByCallback: function (channel) { 42 | if (typeof this.urlReturnCallback !== 'function') { 43 | return false; 44 | } 45 | return this.urlReturnChannels.indexOf(channel) !== -1; 46 | }, 47 | 48 | innerAgreementCallback: function (result, err) { 49 | if (typeof this.userAgreementCallback === 'function') { 50 | if (typeof err === 'undefined') { 51 | err = this.error(); 52 | } 53 | this.userAgreementCallback(result, err); 54 | this.userAgreementCallback = undefined; 55 | } 56 | }, 57 | 58 | innerTransferCallback: function (result, err) { 59 | if (typeof this.userTransferCallback === 'function') { 60 | if (typeof err === 'undefined') { 61 | err = this.error(); 62 | } 63 | this.userTransferCallback(result, err); 64 | this.userTransferCallback = undefined; 65 | } 66 | }, 67 | }; 68 | -------------------------------------------------------------------------------- /src/channels/abc_pay.js: -------------------------------------------------------------------------------- 1 | var utils = require('../utils'); 2 | var callbacks = require('../callbacks'); 3 | module.exports = { 4 | handleCharge: function (charge) { 5 | var credential = charge.credential[charge.channel]; 6 | if (typeof credential === 'string') { 7 | utils.redirectTo(credential, charge.channel); 8 | } else { 9 | callbacks.innerCallback('fail', 10 | callbacks.error('invalid_credential', 'credential 格式不正确')); 11 | } 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /src/channels/abc_pub.js: -------------------------------------------------------------------------------- 1 | var utils = require('../utils'); 2 | var callbacks = require('../callbacks'); 3 | module.exports = { 4 | handleCharge: function (charge) { 5 | var credential = charge.credential[charge.channel]; 6 | if (typeof credential === 'string') { 7 | utils.redirectTo(credential, charge.channel); 8 | } else { 9 | callbacks.innerCallback('fail', 10 | callbacks.error('invalid_credential', 'credential 格式不正确')); 11 | } 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /src/channels/alipay.js: -------------------------------------------------------------------------------- 1 | var callbacks = require('../callbacks'); 2 | var hasOwn = {}.hasOwnProperty; 3 | 4 | /*global my,AlipayJSBridge*/ 5 | module.exports = { 6 | 7 | PINGPP_NOTIFY_URL_BASE: 'https://notify.pingxx.com/notify', 8 | 9 | handleCharge: function (charge) { 10 | var credential = charge.credential[charge.channel]; 11 | if (credential || hasOwn.call(credential, 'orderInfo')) { 12 | this.callpay(credential.orderInfo); 13 | } else { 14 | callbacks.innerCallback('fail', 15 | callbacks.error('invalid_credential', 'missing_alipay')); 16 | } 17 | }, 18 | 19 | alipayLiteEnabled: function () { 20 | return typeof my !== 'undefined' && my.tradePay; 21 | }, 22 | 23 | // 支付宝小程序支付 24 | callpay: function (orderStr) { 25 | var self = this; 26 | if (this.alipayLiteEnabled()) { 27 | return this.alipayLitePay(orderStr); 28 | } 29 | 30 | if (typeof navigator !== 'undefined' 31 | && /AlipayClient/.test(navigator.userAgent || navigator.swuserAgent)) { 32 | return this.waitAlipayJSBridgde(function () { 33 | self.alipayJsBridgePay(orderStr); 34 | }); 35 | } 36 | 37 | var errmsg = '请在支付宝小程序或者支付宝应用内中打开'; 38 | console.log(errmsg); 39 | callbacks.innerCallback('fail', callbacks.error(errmsg)); 40 | return; 41 | }, 42 | 43 | waitAlipayJSBridgde: function (callback) { 44 | if (window.AlipayJSBridge) { 45 | callback && callback(); 46 | } else { 47 | document.addEventListener('AlipayJSBridgeReady', callback, false); 48 | } 49 | }, 50 | 51 | alipayJsBridgePay: function (orderStr) { 52 | AlipayJSBridge.call('tradePay', { 53 | orderStr: orderStr 54 | }, this.alipayResultHandler); 55 | }, 56 | 57 | alipayLitePay: function (orderStr) { 58 | var tradePayParams = {}; 59 | tradePayParams.orderStr = orderStr; 60 | tradePayParams.complete = this.alipayResultHandler; 61 | my.tradePay(tradePayParams); 62 | }, 63 | 64 | alipayResultHandler: function (res) { 65 | var extra = { 66 | resultCode: res.resultCode 67 | }; 68 | if (hasOwn.call(res, 'memo')) { 69 | extra.memo = res.memo; 70 | } 71 | if (hasOwn.call(res, 'result')) { 72 | extra.result = res.result; 73 | } 74 | // 支付成功 75 | if (res.resultCode == '9000') { 76 | callbacks.innerCallback('success', callbacks.error('', extra)); 77 | } else if (res.resultCode == '6001') { // 取消支付 78 | callbacks.innerCallback('cancel', callbacks.error('用户取消支付', extra)); 79 | } else { 80 | callbacks.innerCallback('fail', callbacks.error('支付失败', extra)); 81 | } 82 | }, 83 | 84 | runTestMode: function (charge) { 85 | var path = '/charges/' + charge.id; 86 | my.httpRequest({ 87 | url: this.PINGPP_NOTIFY_URL_BASE + path + '?livemode=false', 88 | success: function(res) { 89 | if (res.data == 'success') { 90 | callbacks.innerCallback('success'); 91 | } else { 92 | callbacks.innerCallback('fail', 93 | callbacks.error('testmode_notify_fail')); 94 | } 95 | }, 96 | fail:function() { 97 | callbacks.innerCallback('fail', callbacks.error('network_err')); 98 | } 99 | }); 100 | } 101 | }; 102 | -------------------------------------------------------------------------------- /src/channels/alipay_lite.js: -------------------------------------------------------------------------------- 1 | var callbacks = require('../callbacks'); 2 | 3 | /*global my*/ 4 | module.exports = { 5 | 6 | PINGPP_NOTIFY_URL_BASE: 'https://notify.pingxx.com/notify', 7 | 8 | handleCharge: function (charge) { 9 | var trade_no = charge.credential[charge.channel]; 10 | if (trade_no) { 11 | this.callpay(trade_no); 12 | } else { 13 | callbacks.innerCallback('fail', 14 | callbacks.error('invalid_credential', 'missing_alipay_lite')); 15 | } 16 | }, 17 | 18 | alipayLiteEnabled: function () { 19 | return typeof my !== 'undefined' && my.tradePay; 20 | }, 21 | 22 | //支付宝小程序支付 23 | callpay: function (tradeNO) { 24 | if (!this.alipayLiteEnabled()) { 25 | console.log('请在支付宝小程序中打开'); 26 | return; 27 | } 28 | var alipay_lite = {}; 29 | alipay_lite.tradeNO = tradeNO; 30 | alipay_lite.complete = function (res) { 31 | //支付成功 32 | if (res.resultCode == 9000) { 33 | callbacks.innerCallback('success'); 34 | } else if (res.resultCode == 6001) { //取消支付 35 | callbacks.innerCallback('cancel', callbacks.error('用户取消支付')); 36 | } else { 37 | callbacks.innerCallback('fail', callbacks.error('支付失败')); 38 | } 39 | }; 40 | my.tradePay(alipay_lite); 41 | }, 42 | 43 | runTestMode: function (charge) { 44 | var path = '/charges/' + charge.id; 45 | my.httpRequest({ 46 | url: this.PINGPP_NOTIFY_URL_BASE + path + '?livemode=false', 47 | success: function(res) { 48 | if (res.data == 'success') { 49 | callbacks.innerCallback('success'); 50 | } else { 51 | callbacks.innerCallback('fail', 52 | callbacks.error('testmode_notify_fail')); 53 | } 54 | }, 55 | fail:function() { 56 | callbacks.innerCallback('fail', callbacks.error('network_err')); 57 | } 58 | }); 59 | } 60 | }; 61 | -------------------------------------------------------------------------------- /src/channels/alipay_pc_direct.js: -------------------------------------------------------------------------------- 1 | var utils = require('../utils'); 2 | var hasOwn = {}.hasOwnProperty; 3 | 4 | module.exports = { 5 | 6 | ALIPAY_PC_DIRECT_URL: 'https://mapi.alipay.com/gateway.do', 7 | 8 | handleCharge: function(charge) { 9 | var channel = charge.channel; 10 | var credential = charge.credential[channel]; 11 | var baseURL = this.ALIPAY_PC_DIRECT_URL; 12 | if (hasOwn.call(credential, 'channel_url')) { 13 | baseURL = credential.channel_url; 14 | } 15 | if (!hasOwn.call(credential, '_input_charset')) { 16 | if(hasOwn.call(credential, 'service') 17 | && credential.service === 'create_direct_pay_by_user') { 18 | credential._input_charset = 'utf-8'; 19 | } 20 | } 21 | var query = utils.stringifyData(credential, channel, true); 22 | utils.redirectTo(baseURL + '?' + query, channel); 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /src/channels/alipay_qr.js: -------------------------------------------------------------------------------- 1 | var hasOwn = {}.hasOwnProperty; 2 | var callbacks = require('../callbacks'); 3 | 4 | /*global AlipayJSBridge*/ 5 | module.exports = { 6 | 7 | handleCharge: function (charge) { 8 | var credential = charge.credential[charge.channel]; 9 | if (hasOwn.call(credential, 'transaction_no')) { 10 | this.tradePay(credential.transaction_no); 11 | } else { 12 | callbacks.innerCallback('fail', 13 | callbacks.error('invalid_credential', 'missing_field_transaction_no')); 14 | } 15 | }, 16 | 17 | ready: function (callback) { 18 | if (window.AlipayJSBridge) { 19 | callback && callback(); 20 | } else { 21 | document.addEventListener('AlipayJSBridgeReady', callback, false); 22 | } 23 | }, 24 | 25 | tradePay: function (tradeNO) { 26 | this.ready(function () { 27 | // 通过传入交易号唤起快捷调用方式(注意tradeNO大小写严格) 28 | AlipayJSBridge.call('tradePay', { 29 | tradeNO: tradeNO 30 | }, function (data) { 31 | if ('9000' == data.resultCode) { 32 | callbacks.innerCallback('success'); 33 | } else if('6001' == data.resultCode) { 34 | callbacks.innerCallback('cancel', callbacks.error(data.result)); 35 | } else { 36 | callbacks.innerCallback('fail', 37 | callbacks.error(data.result)); 38 | } 39 | }); 40 | }); 41 | } 42 | }; 43 | -------------------------------------------------------------------------------- /src/channels/alipay_qr_lakala.js: -------------------------------------------------------------------------------- 1 | var redirectBase = require('./commons/redirect_base'); 2 | 3 | module.exports = { 4 | 5 | handleCharge: function(charge) { 6 | redirectBase.handleCharge(charge); 7 | } 8 | }; 9 | -------------------------------------------------------------------------------- /src/channels/alipay_wap.js: -------------------------------------------------------------------------------- 1 | var utils = require('../utils'); 2 | var mods = require('../mods'); 3 | var hasOwn = {}.hasOwnProperty; 4 | 5 | module.exports = { 6 | 7 | ALIPAY_WAP_URL_OLD: 'https://wappaygw.alipay.com/service/rest.htm', 8 | ALIPAY_WAP_URL: 'https://mapi.alipay.com/gateway.do', 9 | 10 | handleCharge: function(charge) { 11 | var channel = charge.channel; 12 | var credential = charge.credential[channel]; 13 | var baseURL = this.ALIPAY_WAP_URL; 14 | if (hasOwn.call(credential, 'req_data')) { 15 | baseURL = this.ALIPAY_WAP_URL_OLD; 16 | } else if (hasOwn.call(credential, 'channel_url')) { 17 | baseURL = credential.channel_url; 18 | } 19 | if (!hasOwn.call(credential, '_input_charset')) { 20 | if ((hasOwn.call(credential, 'service') 21 | && credential.service === 'alipay.wap.create.direct.pay.by.user') 22 | || hasOwn.call(credential, 'req_data') 23 | ) { 24 | credential._input_charset = 'utf-8'; 25 | } 26 | } 27 | var query = utils.stringifyData(credential, channel, true); 28 | var targetURL = baseURL + '?' + query; 29 | var ap = mods.getExtraModule('ap'); 30 | if (utils.inWeixin() && typeof ap !== 'undefined') { 31 | ap.pay(targetURL); 32 | } else { 33 | utils.redirectTo(targetURL, channel); 34 | } 35 | } 36 | }; -------------------------------------------------------------------------------- /src/channels/alipay_wap_lakala.js: -------------------------------------------------------------------------------- 1 | var redirectBase = require('./commons/redirect_base'); 2 | 3 | module.exports = { 4 | 5 | handleCharge: function(charge) { 6 | redirectBase.handleCharge(charge); 7 | } 8 | }; 9 | -------------------------------------------------------------------------------- /src/channels/bfb_wap.js: -------------------------------------------------------------------------------- 1 | var utils = require('../utils'); 2 | var callbacks = require('../callbacks'); 3 | var hasOwn = {}.hasOwnProperty; 4 | 5 | module.exports = { 6 | 7 | handleCharge: function(charge) { 8 | var channel = charge.channel; 9 | var credential = charge.credential[channel]; 10 | 11 | if (!hasOwn.call(credential, 'url')) { 12 | callbacks.innerCallback('fail', 13 | callbacks.error('invalid_credential', 'missing_field:url')); 14 | return; 15 | } 16 | utils.redirectTo(credential.url + '?' + 17 | utils.stringifyData(credential, channel), channel); 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /src/channels/cb_alipay_pc_direct.js: -------------------------------------------------------------------------------- 1 | var utils = require('../utils'); 2 | var hasOwn = {}.hasOwnProperty; 3 | 4 | module.exports = { 5 | 6 | ALIPAY_PC_DIRECT_URL: 'https://intlmapi.alipay.com/gateway.do', 7 | 8 | handleCharge: function(charge) { 9 | var channel = charge.channel; 10 | var credential = charge.credential[channel]; 11 | var baseURL = this.ALIPAY_PC_DIRECT_URL; 12 | if (hasOwn.call(credential, 'channel_url')) { 13 | baseURL = credential.channel_url; 14 | } 15 | 16 | if (!hasOwn.call(credential, '_input_charset')) { 17 | if(hasOwn.call(credential, 'service') 18 | && credential.service === 'create_forex_trade') { 19 | credential._input_charset = 'utf-8'; 20 | } 21 | } 22 | var query = utils.stringifyData(credential, channel, true); 23 | 24 | utils.redirectTo(baseURL + '?' + query, channel); 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /src/channels/cb_alipay_wap.js: -------------------------------------------------------------------------------- 1 | var utils = require('../utils'); 2 | var mods = require('../mods'); 3 | var hasOwn = {}.hasOwnProperty; 4 | 5 | module.exports = { 6 | 7 | ALIPAY_WAP_URL: 'https://intlmapi.alipay.com/gateway.do', 8 | 9 | handleCharge: function(charge) { 10 | var channel = charge.channel; 11 | var credential = charge.credential[channel]; 12 | var baseURL = this.ALIPAY_WAP_URL; 13 | if (hasOwn.call(credential, 'channel_url')) { 14 | baseURL = credential.channel_url; 15 | } 16 | if (!hasOwn.call(credential, '_input_charset')) { 17 | if ((hasOwn.call(credential, 'service') 18 | && credential.service === 'create_forex_trade_wap') 19 | ) { 20 | credential._input_charset = 'utf-8'; 21 | } 22 | } 23 | 24 | var query = utils.stringifyData(credential, channel, true); 25 | var targetURL = baseURL + '?' + query; 26 | var ap = mods.getExtraModule('ap'); 27 | if (utils.inWeixin() && typeof ap !== 'undefined') { 28 | ap.pay(targetURL); 29 | } else { 30 | utils.redirectTo(targetURL, channel); 31 | } 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /src/channels/cb_wx_pub.js: -------------------------------------------------------------------------------- 1 | var callbacks = require('../callbacks'); 2 | var utils = require('../utils'); 3 | var stash = require('../stash'); 4 | var mods = require('../mods'); 5 | var hasOwn = {}.hasOwnProperty; 6 | 7 | /*global WeixinJSBridge*/ 8 | module.exports = { 9 | 10 | PINGPP_NOTIFY_URL_BASE: 'https://notify.pingxx.com/notify', 11 | 12 | handleCharge: function(charge) { 13 | var credential = charge.credential[charge.channel]; 14 | var fields = [ 15 | 'appId', 'timeStamp', 'nonceStr', 'package', 'signType', 'paySign' 16 | ]; 17 | for (var k = 0; k < fields.length; k++) { 18 | if (!hasOwn.call(credential, fields[k])) { 19 | callbacks.innerCallback('fail', 20 | callbacks.error('invalid_credential', 'missing_field_' + fields[k])); 21 | return; 22 | } 23 | } 24 | stash.jsApiParameters = credential; 25 | this.callpay(); 26 | }, 27 | 28 | callpay: function() { 29 | var self = this; 30 | var wx_jssdk = mods.getExtraModule('wx_jssdk'); 31 | if (typeof wx_jssdk !== 'undefined' && wx_jssdk.jssdkEnabled()) { 32 | wx_jssdk.callpay(); 33 | } else if (typeof WeixinJSBridge == 'undefined') { 34 | var eventCallback = function() { 35 | self.jsApiCall(); 36 | }; 37 | if (document.addEventListener) { 38 | document.addEventListener('WeixinJSBridgeReady', 39 | eventCallback, false); 40 | } else if (document.attachEvent) { 41 | document.attachEvent('WeixinJSBridgeReady', eventCallback); 42 | document.attachEvent('onWeixinJSBridgeReady', eventCallback); 43 | } 44 | } else { 45 | this.jsApiCall(); 46 | } 47 | }, 48 | 49 | jsApiCall: function() { 50 | if (hasOwn.call(stash, 'jsApiParameters')) { 51 | WeixinJSBridge.invoke( 52 | 'getBrandWCPayRequest', 53 | stash.jsApiParameters, 54 | function(res) { 55 | delete stash.jsApiParameters; 56 | if (res.err_msg == 'get_brand_wcpay_request:ok') { 57 | callbacks.innerCallback('success'); 58 | } else if (res.err_msg == 'get_brand_wcpay_request:cancel') { 59 | callbacks.innerCallback('cancel'); 60 | } else { 61 | callbacks.innerCallback('fail', 62 | callbacks.error('wx_result_fail', res.err_msg)); 63 | } 64 | } 65 | ); 66 | } 67 | }, 68 | 69 | runTestMode: function(charge) { 70 | var dopay = confirm('模拟付款?'); 71 | if (dopay) { 72 | var path = '/charges/' + charge.id; 73 | utils.request(this.PINGPP_NOTIFY_URL_BASE + path + '?livemode=false', 74 | 'GET', null, 75 | function(data, status) { 76 | if (status >= 200 && status < 400 && data == 'success') { 77 | callbacks.innerCallback('success'); 78 | } else { 79 | var extra = 'http_code:' + status + ';response:' + data; 80 | callbacks.innerCallback('fail', 81 | callbacks.error('testmode_notify_fail', extra)); 82 | } 83 | }, 84 | function() { 85 | callbacks.innerCallback('fail', callbacks.error('network_err')); 86 | }); 87 | } 88 | } 89 | }; 90 | -------------------------------------------------------------------------------- /src/channels/ccb_qr.js: -------------------------------------------------------------------------------- 1 | var redirectBase = require('./commons/redirect_base'); 2 | 3 | module.exports = { 4 | 5 | handleCharge: function(charge) { 6 | redirectBase.handleCharge(charge); 7 | } 8 | }; 9 | -------------------------------------------------------------------------------- /src/channels/ccb_wap.js: -------------------------------------------------------------------------------- 1 | var utils = require('../utils'); 2 | var hasOwn = {}.hasOwnProperty; 3 | var callbacks = require('../callbacks'); 4 | 5 | module.exports = { 6 | 7 | CCB_WAP_URL_BASE: 'https://ibsbjstar.ccb.com.cn/CCBIS/ccbMain?', 8 | 9 | handleCharge: function(charge) { 10 | var channel = charge.channel; 11 | var credential = charge.credential[channel]; 12 | if (!hasOwn.call(credential, 'orderinfo')) { 13 | callbacks.innerCallback('fail', 14 | callbacks.error('invalid_credential', 'missing_field:orderinfo')); 15 | return; 16 | } 17 | 18 | var targetURL = this.CCB_WAP_URL_BASE + credential['orderinfo']; 19 | utils.redirectTo(targetURL, channel); 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /src/channels/chinaums_alipay_pub.js: -------------------------------------------------------------------------------- 1 | var utils = require('../utils'); 2 | var callbacks = require('../callbacks'); 3 | var hasOwn = {}.hasOwnProperty; 4 | 5 | module.exports = { 6 | handleCharge: function (charge) { 7 | var credential = charge.credential[charge.channel]; 8 | if (typeof credential === 'string') { 9 | utils.redirectTo(credential, charge.channel); 10 | } else { 11 | callbacks.innerCallback('fail', 12 | callbacks.error('invalid_credential', 'credential 格式不正确')); 13 | } 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /src/channels/chinaums_alipay_wap.js: -------------------------------------------------------------------------------- 1 | var utils = require('../utils'); 2 | var callbacks = require('../callbacks'); 3 | var hasOwn = {}.hasOwnProperty; 4 | 5 | module.exports = { 6 | handleCharge: function (charge) { 7 | var credential = charge.credential[charge.channel]; 8 | if (typeof credential === 'string') { 9 | utils.redirectTo(credential, charge.channel); 10 | } else { 11 | callbacks.innerCallback('fail', 12 | callbacks.error('invalid_credential', 'credential 格式不正确')); 13 | } 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /src/channels/chinaums_upacp_wap.js: -------------------------------------------------------------------------------- 1 | var utils = require('../utils'); 2 | var callbacks = require('../callbacks'); 3 | var hasOwn = {}.hasOwnProperty; 4 | 5 | module.exports = { 6 | handleCharge: function (charge) { 7 | var credential = charge.credential[charge.channel]; 8 | if (typeof credential === 'string') { 9 | utils.redirectTo(credential, charge.channel); 10 | } else { 11 | callbacks.innerCallback('fail', 12 | callbacks.error('invalid_credential', 'credential 格式不正确')); 13 | } 14 | } 15 | }; -------------------------------------------------------------------------------- /src/channels/chinaums_wx_pub.js: -------------------------------------------------------------------------------- 1 | var utils = require('../utils'); 2 | var callbacks = require('../callbacks'); 3 | var hasOwn = {}.hasOwnProperty; 4 | 5 | module.exports = { 6 | handleCharge: function (charge) { 7 | var credential = charge.credential[charge.channel]; 8 | if (typeof credential === 'string') { 9 | utils.redirectTo(credential, charge.channel); 10 | } else { 11 | callbacks.innerCallback('fail', 12 | callbacks.error('invalid_credential', 'credential 格式不正确')); 13 | } 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /src/channels/chinaums_wx_wap.js: -------------------------------------------------------------------------------- 1 | var utils = require('../utils'); 2 | var callbacks = require('../callbacks'); 3 | var hasOwn = {}.hasOwnProperty; 4 | 5 | module.exports = { 6 | handleCharge: function (charge) { 7 | var credential = charge.credential[charge.channel]; 8 | if (typeof credential === 'string') { 9 | utils.redirectTo(credential, charge.channel); 10 | } else { 11 | callbacks.innerCallback('fail', 12 | callbacks.error('invalid_credential', 'credential 格式不正确')); 13 | } 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /src/channels/cmb_pc_qr.js: -------------------------------------------------------------------------------- 1 | var utils = require('../utils'); 2 | var hasOwn = {}.hasOwnProperty; 3 | 4 | module.exports = { 5 | 6 | handleCharge: function(charge) { 7 | var channel = charge.channel; 8 | var credential = charge.credential[channel]; 9 | var baseURL; 10 | if (hasOwn.call(credential, 'channel_url')) { 11 | baseURL = credential.channel_url; 12 | delete credential.channel_url; 13 | } 14 | 15 | utils.formSubmit(baseURL, 'post', credential); 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /src/channels/cmb_wallet.js: -------------------------------------------------------------------------------- 1 | var utils = require('../utils'); 2 | var hasOwn = {}.hasOwnProperty; 3 | 4 | module.exports = { 5 | 6 | CMB_WALLET_URL: 7 | 'https://netpay.cmbchina.com/netpayment/BaseHttp.dll?MB_EUserPay', 8 | 9 | handleCharge: function(charge) { 10 | var credential = charge.credential[charge.channel]; 11 | var request_url = this.CMB_WALLET_URL; 12 | if (hasOwn.call(credential, 'ChannelUrl')) { 13 | request_url = credential.ChannelUrl; 14 | delete credential.ChannelUrl; 15 | } 16 | 17 | if (hasOwn.call(credential, 'channelVersion')) { 18 | delete credential.channelVersion; 19 | } 20 | 21 | utils.formSubmit(request_url, 'post', credential); 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /src/channels/commons/redirect_base.js: -------------------------------------------------------------------------------- 1 | var utils = require('../../utils'); 2 | var callbacks = require('../../callbacks'); 3 | var hasOwn = {}.hasOwnProperty; 4 | 5 | module.exports = { 6 | 7 | handleCharge: function(charge) { 8 | var credential = charge.credential[charge.channel]; 9 | var targetURL; 10 | if (typeof credential === 'string') { 11 | targetURL = credential; 12 | } else if (hasOwn.call(credential, 'url')) { 13 | targetURL = credential.url; 14 | } else { 15 | callbacks.innerCallback('fail', callbacks.error('invalid_credential', 16 | 'credential format is incorrect')); 17 | return; 18 | } 19 | utils.redirectTo(targetURL, charge.channel); 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /src/channels/cp_b2b.js: -------------------------------------------------------------------------------- 1 | var utils = require('../utils'); 2 | 3 | module.exports = { 4 | 5 | CP_B2B_URL: 'https://payment.chinapay.com/CTITS/service/rest/page/nref/000000000017/0/0/0/0/0', 6 | 7 | handleCharge: function(charge) { 8 | var credential = charge.credential[charge.channel]; 9 | utils.formSubmit(this.CP_B2B_URL, 'post', credential); 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /src/channels/extras/ap.js: -------------------------------------------------------------------------------- 1 | var stash = require('../../stash'); 2 | var hasOwn = {}.hasOwnProperty; 3 | 4 | (function() { 5 | var b = {}; 6 | var a = {}; 7 | a.PADCHAR = '='; 8 | a.ALPHA = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; 9 | a.makeDOMException = function() { 10 | try { 11 | return new DOMException(DOMException.INVALID_CHARACTER_ERR); 12 | } catch (d) { 13 | var c = new Error('DOM Exception 5'); 14 | c.code = c.number = 5; 15 | c.name = c.description = 'INVALID_CHARACTER_ERR'; 16 | c.toString = function() { 17 | return 'Error: ' + c.name + ': ' + c.message; 18 | }; 19 | return c; 20 | } 21 | }; 22 | a.getbyte64 = function(e, d) { 23 | var c = a.ALPHA.indexOf(e.charAt(d)); 24 | if (c === -1) { 25 | throw a.makeDOMException(); 26 | } 27 | return c; 28 | }; 29 | a.decode = function(f) { 30 | f = '' + f; 31 | var j = a.getbyte64; 32 | var h, e, g; 33 | var d = f.length; 34 | if (d === 0) { 35 | return f; 36 | } 37 | if (d % 4 !== 0) { 38 | throw a.makeDOMException(); 39 | } 40 | h = 0; 41 | if (f.charAt(d - 1) === a.PADCHAR) { 42 | h = 1; 43 | if (f.charAt(d - 2) === a.PADCHAR) { 44 | h = 2; 45 | } 46 | d -= 4; 47 | } 48 | var c = []; 49 | for (e = 0; e < d; e += 4) { 50 | g = (j(f, e) << 18) | 51 | (j(f, e + 1) << 12) | 52 | (j(f, e + 2) << 6) | 53 | j(f, e + 3); 54 | c.push(String.fromCharCode(g >> 16, (g >> 8) & 255, g & 255)); 55 | } 56 | switch (h) { 57 | case 1: 58 | g = (j(f, e) << 18) | (j(f, e + 1) << 12) | (j(f, e + 2) << 6); 59 | c.push(String.fromCharCode(g >> 16, (g >> 8) & 255)); 60 | break; 61 | case 2: 62 | g = (j(f, e) << 18) | (j(f, e + 1) << 12); 63 | c.push(String.fromCharCode(g >> 16)); 64 | break; 65 | } 66 | return c.join(''); 67 | }; 68 | a.getbyte = function(e, d) { 69 | var c = e.charCodeAt(d); 70 | if (c > 255) { 71 | throw a.makeDOMException(); 72 | } 73 | return c; 74 | }; 75 | a.encode = function(f) { 76 | if (arguments.length !== 1) { 77 | throw new SyntaxError('Not enough arguments'); 78 | } 79 | var g = a.PADCHAR; 80 | var h = a.ALPHA; 81 | var k = a.getbyte; 82 | var e, j; 83 | var c = []; 84 | f = '' + f; 85 | var d = f.length - f.length % 3; 86 | if (f.length === 0) { 87 | return f; 88 | } 89 | for (e = 0; e < d; e += 3) { 90 | j = (k(f, e) << 16) | (k(f, e + 1) << 8) | k(f, e + 2); 91 | c.push(h.charAt(j >> 18)); 92 | c.push(h.charAt((j >> 12) & 63)); 93 | c.push(h.charAt((j >> 6) & 63)); 94 | c.push(h.charAt(j & 63)); 95 | } 96 | switch (f.length - d) { 97 | case 1: 98 | j = k(f, e) << 16; 99 | c.push(h.charAt(j >> 18) + h.charAt((j >> 12) & 63) + g + g); 100 | break; 101 | case 2: 102 | j = (k(f, e) << 16) | (k(f, e + 1) << 8); 103 | c.push(h.charAt(j >> 18) + 104 | h.charAt((j >> 12) & 63) + 105 | h.charAt((j >> 6) & 63) + g); 106 | break; 107 | } 108 | return c.join(''); 109 | }; 110 | b.url = 'pay.htm'; 111 | b.pay = function(d) { 112 | var c = encodeURIComponent(a.encode(d)); 113 | if (hasOwn.call(stash, 'APURL')) { 114 | b.url = stash.APURL; 115 | } 116 | location.href = b.url + '?goto=' + c; 117 | }; 118 | b.decode = function(c) { 119 | return a.decode(decodeURIComponent(c)); 120 | }; 121 | 122 | module.exports = b; 123 | })(); 124 | -------------------------------------------------------------------------------- /src/channels/extras/wx_jssdk.js: -------------------------------------------------------------------------------- 1 | var stash = require('../../stash'); 2 | var callbacks = require('../../callbacks'); 3 | 4 | /*global wx*/ 5 | module.exports = { 6 | 7 | jssdkEnabled: function() { 8 | return typeof wx !== 'undefined' && typeof stash.signature !== 'undefined'; 9 | }, 10 | 11 | callpay: function() { 12 | var wxConfigFailed = false; 13 | wx.config({ 14 | debug: typeof stash.debug == 'boolean' ? stash.debug : false, 15 | appId: stash.jsApiParameters.appId, 16 | timestamp: stash.jsApiParameters.timeStamp, 17 | nonceStr: stash.jsApiParameters.nonceStr, 18 | signature: stash.signature, 19 | jsApiList: ['chooseWXPay'] 20 | }); 21 | delete stash.signature; 22 | delete stash.debug; 23 | wx.ready(function() { 24 | if (wxConfigFailed) { 25 | return; 26 | } 27 | wx.chooseWXPay({ 28 | timestamp: stash.jsApiParameters.timeStamp, 29 | nonceStr: stash.jsApiParameters.nonceStr, 30 | 'package': stash.jsApiParameters.package, 31 | signType: stash.jsApiParameters.signType, 32 | paySign: stash.jsApiParameters.paySign, 33 | success: function(res) { 34 | delete stash.jsApiParameters; 35 | if (res.errMsg == 'chooseWXPay:ok') { 36 | callbacks.innerCallback('success'); 37 | } else { 38 | callbacks.innerCallback('fail', 39 | callbacks.error('wx_result_fail', res.errMsg)); 40 | } 41 | }, 42 | /* eslint-disable no-unused-vars */ 43 | cancel: function(res) { 44 | delete stash.jsApiParameters; 45 | callbacks.innerCallback('cancel'); 46 | }, 47 | /* eslint-enable no-unused-vars */ 48 | fail: function(res) { 49 | delete stash.jsApiParameters; 50 | callbacks.innerCallback('fail', 51 | callbacks.error('wx_result_fail', res.errMsg)); 52 | } 53 | }); 54 | }); 55 | wx.error(function(res) { 56 | wxConfigFailed = true; 57 | delete stash.jsApiParameters; 58 | callbacks.innerCallback('fail', 59 | callbacks.error('wx_config_error', res.errMsg)); 60 | }); 61 | } 62 | }; 63 | -------------------------------------------------------------------------------- /src/channels/isv_lite.js: -------------------------------------------------------------------------------- 1 | var wxLite = require('./wx_lite'); 2 | 3 | module.exports = wxLite; 4 | -------------------------------------------------------------------------------- /src/channels/isv_wap.js: -------------------------------------------------------------------------------- 1 | var redirectBase = require('./commons/redirect_base'); 2 | var callbacks = require('../callbacks'); 3 | var utils = require('../utils'); 4 | var hasOwn = {}.hasOwnProperty; 5 | 6 | module.exports = { 7 | handleCharge: function (charge) { 8 | var extra = charge.extra; 9 | if (hasOwn.call(extra, 'pay_channel')) { 10 | var pay_channel = extra.pay_channel; 11 | if (pay_channel === 'wx' && !utils.inWeixin()) { 12 | callbacks.innerCallback('fail', 13 | callbacks.error('Not in the WeChat browser')); 14 | return; 15 | } else if (pay_channel === 'alipay' && !utils.inAlipay()) { 16 | callbacks.innerCallback('fail', 17 | callbacks.error('Not in the Alipay browser')); 18 | return; 19 | } 20 | } else { 21 | callbacks.innerCallback('fail', 22 | callbacks.error('invalid_charge', 'charge 格式不正确')); 23 | return; 24 | } 25 | redirectBase.handleCharge(charge); 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /src/channels/jdpay_wap.js: -------------------------------------------------------------------------------- 1 | var utils = require('../utils'); 2 | var hasOwn = {}.hasOwnProperty; 3 | 4 | module.exports = { 5 | 6 | JDPAY_WAP_URL_OLD: 'https://m.jdpay.com/wepay/web/pay', 7 | JDPAY_H5_URL: 'https://h5pay.jd.com/jdpay/saveOrder', 8 | JDPAY_PC_URL: 'https://wepay.jd.com/jdpay/saveOrder', 9 | 10 | handleCharge: function(charge) { 11 | var credential = charge.credential[charge.channel]; 12 | var request_url = this.JDPAY_H5_URL; 13 | if (hasOwn.call(credential, 'channelUrl')) { 14 | request_url = credential.channelUrl; 15 | delete credential.channelUrl; 16 | } else if (hasOwn.call(credential, 'merchantRemark')) { 17 | request_url = this.JDPAY_WAP_URL_OLD; 18 | } 19 | utils.formSubmit(request_url, 'post', credential); 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /src/channels/nucc_b2b_lakala.js: -------------------------------------------------------------------------------- 1 | var utils = require('../utils'); 2 | var hasOwn = {}.hasOwnProperty; 3 | 4 | module.exports = { 5 | handleCharge: function (charge) { 6 | var credential = charge.credential[charge.channel]; 7 | if (hasOwn.call(credential, 'bankUrl')) { 8 | request_url = credential.bankUrl; 9 | delete credential.bankUrl; 10 | } 11 | 12 | utils.formSubmit(request_url, 'post', credential); 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /src/channels/nucc_b2c_lakala.js: -------------------------------------------------------------------------------- 1 | var utils = require('../utils'); 2 | var hasOwn = {}.hasOwnProperty; 3 | 4 | module.exports = { 5 | handleCharge: function (charge) { 6 | var credential = charge.credential[charge.channel]; 7 | if (hasOwn.call(credential, 'bankUrl')) { 8 | request_url = credential.bankUrl; 9 | delete credential.bankUrl; 10 | } 11 | 12 | utils.formSubmit(request_url, 'post', credential); 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /src/channels/pab_pc.js: -------------------------------------------------------------------------------- 1 | var utils = require('../utils'); 2 | 3 | module.exports = { 4 | 5 | handleCharge: function(charge) { 6 | var credential = charge.credential[charge.channel]; 7 | var request_url = credential.channelUrl; 8 | delete credential.channelUrl; 9 | 10 | utils.formSubmit(request_url, 'post', credential); 11 | } 12 | }; 13 | -------------------------------------------------------------------------------- /src/channels/paypal.js: -------------------------------------------------------------------------------- 1 | var utils = require('../utils'); 2 | 3 | module.exports = { 4 | handleCharge: function(charge) { 5 | var credential = charge.credential[charge.channel]; 6 | utils.redirectTo(credential, charge.channel); 7 | } 8 | }; 9 | -------------------------------------------------------------------------------- /src/channels/qpay_pub.js: -------------------------------------------------------------------------------- 1 | var callbacks = require('../callbacks'); 2 | var utils = require('../utils'); 3 | var stash = require('../stash'); 4 | var hasOwn = {}.hasOwnProperty; 5 | 6 | /*global mqq*/ 7 | module.exports = { 8 | SRC_URL: 'https://open.mobile.qq.com/sdk/qqapi.js?_bid=152', 9 | ID: 'mqq_api', 10 | 11 | handleCharge: function (charge) { 12 | var credential = charge.credential[charge.channel]; 13 | 14 | if (!hasOwn.call(credential, 'token_id')) { 15 | callbacks.innerCallback('fail', 16 | callbacks.error('invalid_credential', 'missing_token_id')); 17 | return; 18 | } 19 | stash.tokenId = credential.token_id; 20 | utils.loadUrlJs(this.ID, this.SRC_URL, this.callpay); 21 | }, 22 | 23 | callpay: function () { 24 | if (typeof mqq != 'undefined') { 25 | if (mqq.QQVersion == 0) { 26 | callbacks.innerCallback('fail', 27 | callbacks.error('Not in the QQ client')); 28 | delete stash.tokenId; 29 | return; 30 | } 31 | mqq.tenpay.pay({ 32 | tokenId: stash.tokenId 33 | }, function (result) { 34 | if (result.resultCode == 0) { 35 | callbacks.innerCallback('success'); 36 | } else { 37 | callbacks.innerCallback('fail', 38 | callbacks.error(result.retmsg)); 39 | } 40 | }); 41 | } else { 42 | callbacks.innerCallback('fail', 43 | callbacks.error('network_err')); 44 | } 45 | delete stash.tokenId; 46 | } 47 | }; -------------------------------------------------------------------------------- /src/channels/upacp_b2b.js: -------------------------------------------------------------------------------- 1 | var utils = require('../utils'); 2 | var hasOwn = {}.hasOwnProperty; 3 | 4 | module.exports = { 5 | 6 | handleCharge: function(charge) { 7 | var channel = charge.channel; 8 | var credential = charge.credential[channel]; 9 | var baseURL; 10 | if (hasOwn.call(credential, 'channel_url')) { 11 | baseURL = credential.channel_url; 12 | delete credential.channel_url; 13 | } 14 | 15 | utils.formSubmit(baseURL, 'post', credential); 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /src/channels/upacp_pc.js: -------------------------------------------------------------------------------- 1 | var utils = require('../utils'); 2 | 3 | module.exports = { 4 | 5 | UPACP_PC_URL: 'https://gateway.95516.com/gateway/api/frontTransReq.do', 6 | 7 | handleCharge: function(charge) { 8 | var credential = charge.credential[charge.channel]; 9 | utils.formSubmit(this.UPACP_PC_URL, 'post', credential); 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /src/channels/upacp_wap.js: -------------------------------------------------------------------------------- 1 | var utils = require('../utils'); 2 | 3 | module.exports = { 4 | 5 | UPACP_WAP_URL: 'https://gateway.95516.com/gateway/api/frontTransReq.do', 6 | 7 | handleCharge: function(charge) { 8 | var credential = charge.credential[charge.channel]; 9 | utils.formSubmit(this.UPACP_WAP_URL, 'post', credential); 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /src/channels/wx_lite.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by yulitao on 2016/12/29. 3 | */ 4 | var stash = require('../stash'); 5 | var callbacks = require('../callbacks'); 6 | var hasOwn = {}.hasOwnProperty; 7 | /*global wx*/ 8 | module.exports = { 9 | 10 | PINGPP_NOTIFY_URL_BASE: 'https://notify.pingxx.com/notify', 11 | 12 | handleCharge: function (charge) { 13 | var credential = charge.credential[charge.channel]; 14 | var fields = [ 15 | 'appId', 'timeStamp', 'nonceStr', 'package', 'signType', 'paySign' 16 | ]; 17 | for (var k = 0; k < fields.length; k++) { 18 | if (!hasOwn.call(credential, fields[k])) { 19 | callbacks.innerCallback('fail', 20 | callbacks.error('invalid_credential', 'missing_field_' + fields[k])); 21 | return; 22 | } 23 | } 24 | stash.jsApiParameters = credential; 25 | this.callpay(); 26 | }, 27 | 28 | wxLiteEnabled: function () { 29 | return typeof wx !== 'undefined' && wx.requestPayment; 30 | }, 31 | 32 | //微信小程序支付 33 | callpay: function () { 34 | if (!this.wxLiteEnabled()) { 35 | console.log('请在微信小程序中打开'); 36 | return; 37 | } 38 | var wx_lite = stash.jsApiParameters; 39 | delete wx_lite.appId; 40 | wx_lite.complete = function (res) { 41 | //支付成功 42 | if (res.errMsg === 'requestPayment:ok') { 43 | callbacks.innerCallback('success'); 44 | } 45 | //取消支付 46 | if (res.errMsg === 'requestPayment:cancel' 47 | || res.errMsg === 'requestPayment:fail cancel') { 48 | callbacks.innerCallback('cancel', callbacks.error('用户取消支付')); 49 | } 50 | //支付验证签名失败 51 | if (res.err_code !== 'undefined' && res.err_desc !== 'undefined') { 52 | callbacks.innerCallback('fail', callbacks.error(res.err_desc, res)); 53 | } 54 | }; 55 | wx.requestPayment(wx_lite); 56 | }, 57 | 58 | runTestMode: function (charge) { 59 | var path = '/charges/' + charge.id; 60 | wx.request({ 61 | url: this.PINGPP_NOTIFY_URL_BASE + path + '?livemode=false', 62 | success: function(res) { 63 | if (res.data == 'success') { 64 | callbacks.innerCallback('success'); 65 | } else { 66 | callbacks.innerCallback('fail', 67 | callbacks.error('testmode_notify_fail')); 68 | } 69 | }, 70 | fail:function() { 71 | callbacks.innerCallback('fail', callbacks.error('network_err')); 72 | } 73 | }); 74 | } 75 | }; 76 | -------------------------------------------------------------------------------- /src/channels/wx_lite_pab.js: -------------------------------------------------------------------------------- 1 | var wxLite = require('./wx_lite'); 2 | 3 | module.exports = wxLite; 4 | -------------------------------------------------------------------------------- /src/channels/wx_pub.js: -------------------------------------------------------------------------------- 1 | var callbacks = require('../callbacks'); 2 | var utils = require('../utils'); 3 | var stash = require('../stash'); 4 | var mods = require('../mods'); 5 | var hasOwn = {}.hasOwnProperty; 6 | 7 | /*global WeixinJSBridge*/ 8 | module.exports = { 9 | 10 | PINGPP_NOTIFY_URL_BASE: 'https://notify.pingxx.com/notify', 11 | 12 | handleCharge: function(charge) { 13 | var credential = charge.credential[charge.channel]; 14 | var fields = [ 15 | 'appId', 'timeStamp', 'nonceStr', 'package', 'signType', 'paySign' 16 | ]; 17 | for (var k = 0; k < fields.length; k++) { 18 | if (!hasOwn.call(credential, fields[k])) { 19 | callbacks.innerCallback('fail', 20 | callbacks.error('invalid_credential', 'missing_field_' + fields[k])); 21 | return; 22 | } 23 | } 24 | stash.jsApiParameters = credential; 25 | this.callpay(); 26 | }, 27 | 28 | callpay: function() { 29 | var self = this; 30 | var wx_jssdk = mods.getExtraModule('wx_jssdk'); 31 | if (typeof wx_jssdk !== 'undefined' && wx_jssdk.jssdkEnabled()) { 32 | wx_jssdk.callpay(); 33 | } else if (typeof WeixinJSBridge == 'undefined') { 34 | var eventCallback = function() { 35 | self.jsApiCall(); 36 | }; 37 | if (document.addEventListener) { 38 | document.addEventListener('WeixinJSBridgeReady', 39 | eventCallback, false); 40 | } else if (document.attachEvent) { 41 | document.attachEvent('WeixinJSBridgeReady', eventCallback); 42 | document.attachEvent('onWeixinJSBridgeReady', eventCallback); 43 | } 44 | } else { 45 | this.jsApiCall(); 46 | } 47 | }, 48 | 49 | jsApiCall: function() { 50 | if (hasOwn.call(stash, 'jsApiParameters')) { 51 | WeixinJSBridge.invoke( 52 | 'getBrandWCPayRequest', 53 | stash.jsApiParameters, 54 | function(res) { 55 | delete stash.jsApiParameters; 56 | if (res.err_msg == 'get_brand_wcpay_request:ok') { 57 | callbacks.innerCallback('success'); 58 | } else if (res.err_msg == 'get_brand_wcpay_request:cancel') { 59 | callbacks.innerCallback('cancel'); 60 | } else { 61 | callbacks.innerCallback('fail', 62 | callbacks.error('wx_result_fail', res.err_msg)); 63 | } 64 | } 65 | ); 66 | } 67 | }, 68 | 69 | runTestMode: function(charge) { 70 | var dopay = confirm('模拟付款?'); 71 | if (dopay) { 72 | var path = '/charges/' + charge.id; 73 | utils.request(this.PINGPP_NOTIFY_URL_BASE + path + '?livemode=false', 74 | 'GET', null, 75 | function(data, status) { 76 | if (status >= 200 && status < 400 && data == 'success') { 77 | callbacks.innerCallback('success'); 78 | } else { 79 | var extra = 'http_code:' + status + ';response:' + data; 80 | callbacks.innerCallback('fail', 81 | callbacks.error('testmode_notify_fail', extra)); 82 | } 83 | }, 84 | function() { 85 | callbacks.innerCallback('fail', callbacks.error('network_err')); 86 | }); 87 | } 88 | } 89 | }; 90 | -------------------------------------------------------------------------------- /src/channels/wx_pub_pab.js: -------------------------------------------------------------------------------- 1 | var wxPub = require('./wx_pub'); 2 | 3 | module.exports = wxPub; 4 | -------------------------------------------------------------------------------- /src/channels/wx_wap.js: -------------------------------------------------------------------------------- 1 | var utils = require('../utils'); 2 | var callbacks = require('../callbacks'); 3 | var hasOwn = {}.hasOwnProperty; 4 | 5 | module.exports = { 6 | 7 | handleCharge: function(charge) { 8 | var credential = charge.credential[charge.channel]; 9 | if (typeof credential === 'string') { 10 | utils.redirectTo(credential, charge.channel); 11 | } else if (typeof credential === 'object' 12 | && hasOwn.call(credential, 'url') 13 | ) { 14 | utils.redirectTo(credential.url, charge.channel); 15 | } else { 16 | callbacks.innerCallback('fail', 17 | callbacks.error('invalid_credential', 'credential 格式不正确')); 18 | } 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /src/channels/yeepay_wap.js: -------------------------------------------------------------------------------- 1 | var utils = require('../utils'); 2 | var callbacks = require('../callbacks'); 3 | var hasOwn = {}.hasOwnProperty; 4 | 5 | module.exports = { 6 | 7 | YEEPAY_WAP_URL: 'https://ok.yeepay.com/paymobile/api/pay/request', 8 | YEEPAY_WAP_TEST_URL: 'https://mobiletest.yeepay.com/paymobile/api/pay/request', 9 | 10 | handleCharge: function(charge) { 11 | var channel = charge.channel; 12 | var credential = charge.credential[channel]; 13 | var fields = ['merchantaccount', 'encryptkey', 'data']; 14 | for (var k = 0; k < fields.length; k++) { 15 | if (!hasOwn.call(credential, fields[k])) { 16 | callbacks.innerCallback('fail', 17 | callbacks.error('invalid_credential', 'missing_field_' + fields[k])); 18 | return; 19 | } 20 | } 21 | var baseURL; 22 | if (hasOwn.call(credential, 'mode') && credential.mode == 'test') { 23 | baseURL = this.YEEPAY_WAP_TEST_URL; 24 | } else { 25 | baseURL = this.YEEPAY_WAP_URL; 26 | } 27 | utils.redirectTo(baseURL + '?' + 28 | utils.stringifyData(credential, channel, true), charge.channel); 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /src/channels/yeepay_wx_lite.js: -------------------------------------------------------------------------------- 1 | var wxPub = require('./wx_lite'); 2 | 3 | module.exports = wxPub; 4 | -------------------------------------------------------------------------------- /src/channels/yeepay_wx_lite_ofl.js: -------------------------------------------------------------------------------- 1 | var wxPub = require('./wx_lite'); 2 | 3 | module.exports = wxPub; 4 | -------------------------------------------------------------------------------- /src/channels/yeepay_wx_pub.js: -------------------------------------------------------------------------------- 1 | var wxPub = require('./wx_pub'); 2 | 3 | module.exports = wxPub; 4 | -------------------------------------------------------------------------------- /src/channels/yeepay_wx_pub_ofl.js: -------------------------------------------------------------------------------- 1 | var wxPub = require('./wx_pub'); 2 | 3 | module.exports = wxPub; 4 | -------------------------------------------------------------------------------- /src/errors.js: -------------------------------------------------------------------------------- 1 | var error = function PingppError(message, extra) { 2 | this.message = message; 3 | this.extra = extra; 4 | }; 5 | 6 | module.exports = { 7 | Error: error, 8 | }; 9 | -------------------------------------------------------------------------------- /src/init.js: -------------------------------------------------------------------------------- 1 | var stash = require('./stash'); 2 | var utils = require('./utils'); 3 | 4 | module.exports = { 5 | init: function() { 6 | var self = this; 7 | utils.documentReady(function(){ 8 | try { 9 | (!(utils.inWxLite() || utils.inAlipayLite())) && self.initPuid(); 10 | } catch (e){/* empty */} 11 | }); 12 | }, 13 | 14 | initPuid: function() { 15 | if (typeof window === 'undefined' || typeof localStorage === 'undefined' 16 | || localStorage === null) { 17 | return; 18 | } 19 | var puid = localStorage.getItem('pingpp_uid'); 20 | if (puid === null) { 21 | puid = utils.randomString(); 22 | try { 23 | localStorage.setItem('pingpp_uid', puid); 24 | } catch (e) { 25 | /* empty */ 26 | } 27 | } 28 | stash.puid = puid; 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /src/libs/md5.js: -------------------------------------------------------------------------------- 1 | /* 2 | * JavaScript MD5 3 | * https://github.com/blueimp/JavaScript-MD5 4 | * 5 | * Copyright 2011, Sebastian Tschan 6 | * https://blueimp.net 7 | * 8 | * Licensed under the MIT license: 9 | * http://www.opensource.org/licenses/MIT 10 | * 11 | * Based on 12 | * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message 13 | * Digest Algorithm, as defined in RFC 1321. 14 | * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009 15 | * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet 16 | * Distributed under the BSD License 17 | * See http://pajhome.org.uk/crypt/md5 for more info. 18 | */ 19 | 20 | /* global unescape, module */ 21 | 22 | (function () { 23 | /* 24 | * Add integers, wrapping at 2^32. This uses 16-bit operations internally 25 | * to work around bugs in some JS interpreters. 26 | */ 27 | function safe_add (x, y) { 28 | var lsw = (x & 0xFFFF) + (y & 0xFFFF); 29 | var msw = (x >> 16) + (y >> 16) + (lsw >> 16); 30 | return (msw << 16) | (lsw & 0xFFFF); 31 | } 32 | 33 | /* 34 | * Bitwise rotate a 32-bit number to the left. 35 | */ 36 | function bit_rol (num, cnt) { 37 | return (num << cnt) | (num >>> (32 - cnt)); 38 | } 39 | 40 | /* 41 | * These functions implement the four basic operations the algorithm uses. 42 | */ 43 | function md5_cmn (q, a, b, x, s, t) { 44 | return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s), b); 45 | } 46 | function md5_ff (a, b, c, d, x, s, t) { 47 | return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t); 48 | } 49 | function md5_gg (a, b, c, d, x, s, t) { 50 | return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t); 51 | } 52 | function md5_hh (a, b, c, d, x, s, t) { 53 | return md5_cmn(b ^ c ^ d, a, b, x, s, t); 54 | } 55 | function md5_ii (a, b, c, d, x, s, t) { 56 | return md5_cmn(c ^ (b | (~d)), a, b, x, s, t); 57 | } 58 | 59 | /* 60 | * Calculate the MD5 of an array of little-endian words, and a bit length. 61 | */ 62 | function binl_md5 (x, len) { 63 | /* append padding */ 64 | x[len >> 5] |= 0x80 << (len % 32); 65 | x[(((len + 64) >>> 9) << 4) + 14] = len; 66 | 67 | var i; 68 | var olda; 69 | var oldb; 70 | var oldc; 71 | var oldd; 72 | var a = 1732584193; 73 | var b = -271733879; 74 | var c = -1732584194; 75 | var d = 271733878; 76 | 77 | for (i = 0; i < x.length; i += 16) { 78 | olda = a; 79 | oldb = b; 80 | oldc = c; 81 | oldd = d; 82 | 83 | a = md5_ff(a, b, c, d, x[i], 7, -680876936); 84 | d = md5_ff(d, a, b, c, x[i + 1], 12, -389564586); 85 | c = md5_ff(c, d, a, b, x[i + 2], 17, 606105819); 86 | b = md5_ff(b, c, d, a, x[i + 3], 22, -1044525330); 87 | a = md5_ff(a, b, c, d, x[i + 4], 7, -176418897); 88 | d = md5_ff(d, a, b, c, x[i + 5], 12, 1200080426); 89 | c = md5_ff(c, d, a, b, x[i + 6], 17, -1473231341); 90 | b = md5_ff(b, c, d, a, x[i + 7], 22, -45705983); 91 | a = md5_ff(a, b, c, d, x[i + 8], 7, 1770035416); 92 | d = md5_ff(d, a, b, c, x[i + 9], 12, -1958414417); 93 | c = md5_ff(c, d, a, b, x[i + 10], 17, -42063); 94 | b = md5_ff(b, c, d, a, x[i + 11], 22, -1990404162); 95 | a = md5_ff(a, b, c, d, x[i + 12], 7, 1804603682); 96 | d = md5_ff(d, a, b, c, x[i + 13], 12, -40341101); 97 | c = md5_ff(c, d, a, b, x[i + 14], 17, -1502002290); 98 | b = md5_ff(b, c, d, a, x[i + 15], 22, 1236535329); 99 | 100 | a = md5_gg(a, b, c, d, x[i + 1], 5, -165796510); 101 | d = md5_gg(d, a, b, c, x[i + 6], 9, -1069501632); 102 | c = md5_gg(c, d, a, b, x[i + 11], 14, 643717713); 103 | b = md5_gg(b, c, d, a, x[i], 20, -373897302); 104 | a = md5_gg(a, b, c, d, x[i + 5], 5, -701558691); 105 | d = md5_gg(d, a, b, c, x[i + 10], 9, 38016083); 106 | c = md5_gg(c, d, a, b, x[i + 15], 14, -660478335); 107 | b = md5_gg(b, c, d, a, x[i + 4], 20, -405537848); 108 | a = md5_gg(a, b, c, d, x[i + 9], 5, 568446438); 109 | d = md5_gg(d, a, b, c, x[i + 14], 9, -1019803690); 110 | c = md5_gg(c, d, a, b, x[i + 3], 14, -187363961); 111 | b = md5_gg(b, c, d, a, x[i + 8], 20, 1163531501); 112 | a = md5_gg(a, b, c, d, x[i + 13], 5, -1444681467); 113 | d = md5_gg(d, a, b, c, x[i + 2], 9, -51403784); 114 | c = md5_gg(c, d, a, b, x[i + 7], 14, 1735328473); 115 | b = md5_gg(b, c, d, a, x[i + 12], 20, -1926607734); 116 | 117 | a = md5_hh(a, b, c, d, x[i + 5], 4, -378558); 118 | d = md5_hh(d, a, b, c, x[i + 8], 11, -2022574463); 119 | c = md5_hh(c, d, a, b, x[i + 11], 16, 1839030562); 120 | b = md5_hh(b, c, d, a, x[i + 14], 23, -35309556); 121 | a = md5_hh(a, b, c, d, x[i + 1], 4, -1530992060); 122 | d = md5_hh(d, a, b, c, x[i + 4], 11, 1272893353); 123 | c = md5_hh(c, d, a, b, x[i + 7], 16, -155497632); 124 | b = md5_hh(b, c, d, a, x[i + 10], 23, -1094730640); 125 | a = md5_hh(a, b, c, d, x[i + 13], 4, 681279174); 126 | d = md5_hh(d, a, b, c, x[i], 11, -358537222); 127 | c = md5_hh(c, d, a, b, x[i + 3], 16, -722521979); 128 | b = md5_hh(b, c, d, a, x[i + 6], 23, 76029189); 129 | a = md5_hh(a, b, c, d, x[i + 9], 4, -640364487); 130 | d = md5_hh(d, a, b, c, x[i + 12], 11, -421815835); 131 | c = md5_hh(c, d, a, b, x[i + 15], 16, 530742520); 132 | b = md5_hh(b, c, d, a, x[i + 2], 23, -995338651); 133 | 134 | a = md5_ii(a, b, c, d, x[i], 6, -198630844); 135 | d = md5_ii(d, a, b, c, x[i + 7], 10, 1126891415); 136 | c = md5_ii(c, d, a, b, x[i + 14], 15, -1416354905); 137 | b = md5_ii(b, c, d, a, x[i + 5], 21, -57434055); 138 | a = md5_ii(a, b, c, d, x[i + 12], 6, 1700485571); 139 | d = md5_ii(d, a, b, c, x[i + 3], 10, -1894986606); 140 | c = md5_ii(c, d, a, b, x[i + 10], 15, -1051523); 141 | b = md5_ii(b, c, d, a, x[i + 1], 21, -2054922799); 142 | a = md5_ii(a, b, c, d, x[i + 8], 6, 1873313359); 143 | d = md5_ii(d, a, b, c, x[i + 15], 10, -30611744); 144 | c = md5_ii(c, d, a, b, x[i + 6], 15, -1560198380); 145 | b = md5_ii(b, c, d, a, x[i + 13], 21, 1309151649); 146 | a = md5_ii(a, b, c, d, x[i + 4], 6, -145523070); 147 | d = md5_ii(d, a, b, c, x[i + 11], 10, -1120210379); 148 | c = md5_ii(c, d, a, b, x[i + 2], 15, 718787259); 149 | b = md5_ii(b, c, d, a, x[i + 9], 21, -343485551); 150 | 151 | a = safe_add(a, olda); 152 | b = safe_add(b, oldb); 153 | c = safe_add(c, oldc); 154 | d = safe_add(d, oldd); 155 | } 156 | return [a, b, c, d]; 157 | } 158 | 159 | /* 160 | * Convert an array of little-endian words to a string 161 | */ 162 | function binl2rstr (input) { 163 | var i; 164 | var output = ''; 165 | for (i = 0; i < input.length * 32; i += 8) { 166 | output += String.fromCharCode((input[i >> 5] >>> (i % 32)) & 0xFF); 167 | } 168 | return output; 169 | } 170 | 171 | /* 172 | * Convert a raw string to an array of little-endian words 173 | * Characters >255 have their high-byte silently ignored. 174 | */ 175 | function rstr2binl (input) { 176 | var i; 177 | var output = []; 178 | output[(input.length >> 2) - 1] = undefined; 179 | for (i = 0; i < output.length; i += 1) { 180 | output[i] = 0; 181 | } 182 | for (i = 0; i < input.length * 8; i += 8) { 183 | output[i >> 5] |= (input.charCodeAt(i / 8) & 0xFF) << (i % 32); 184 | } 185 | return output; 186 | } 187 | 188 | /* 189 | * Calculate the MD5 of a raw string 190 | */ 191 | function rstr_md5 (s) { 192 | return binl2rstr(binl_md5(rstr2binl(s), s.length * 8)); 193 | } 194 | 195 | /* 196 | * Calculate the HMAC-MD5, of a key and some data (raw strings) 197 | */ 198 | function rstr_hmac_md5 (key, data) { 199 | var i; 200 | var bkey = rstr2binl(key); 201 | var ipad = []; 202 | var opad = []; 203 | var hash; 204 | ipad[15] = opad[15] = undefined; 205 | if (bkey.length > 16) { 206 | bkey = binl_md5(bkey, key.length * 8); 207 | } 208 | for (i = 0; i < 16; i += 1) { 209 | ipad[i] = bkey[i] ^ 0x36363636; 210 | opad[i] = bkey[i] ^ 0x5C5C5C5C; 211 | } 212 | hash = binl_md5(ipad.concat(rstr2binl(data)), 512 + data.length * 8); 213 | return binl2rstr(binl_md5(opad.concat(hash), 512 + 128)); 214 | } 215 | 216 | /* 217 | * Convert a raw string to a hex string 218 | */ 219 | function rstr2hex (input) { 220 | var hex_tab = '0123456789abcdef'; 221 | var output = ''; 222 | var x; 223 | var i; 224 | for (i = 0; i < input.length; i += 1) { 225 | x = input.charCodeAt(i); 226 | output += hex_tab.charAt((x >>> 4) & 0x0F) + 227 | hex_tab.charAt(x & 0x0F); 228 | } 229 | return output; 230 | } 231 | 232 | /* 233 | * Encode a string as utf-8 234 | */ 235 | function str2rstr_utf8 (input) { 236 | return unescape(encodeURIComponent(input)); 237 | } 238 | 239 | /* 240 | * Take string arguments and return either raw or hex encoded strings 241 | */ 242 | function raw_md5 (s) { 243 | return rstr_md5(str2rstr_utf8(s)); 244 | } 245 | function hex_md5 (s) { 246 | return rstr2hex(raw_md5(s)); 247 | } 248 | function raw_hmac_md5 (k, d) { 249 | return rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d)); 250 | } 251 | function hex_hmac_md5 (k, d) { 252 | return rstr2hex(raw_hmac_md5(k, d)); 253 | } 254 | 255 | function md5 (string, key, raw) { 256 | if (!key) { 257 | if (!raw) { 258 | return hex_md5(string); 259 | } 260 | return raw_md5(string); 261 | } 262 | if (!raw) { 263 | return hex_hmac_md5(key, string); 264 | } 265 | return raw_hmac_md5(key, string); 266 | } 267 | 268 | module.exports = md5; 269 | }()); 270 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | var version = require('./version').v; 2 | var hasOwn = {}.hasOwnProperty; 3 | 4 | var PingppSDK = function () { 5 | require('./init').init(); 6 | }; 7 | 8 | PingppSDK.prototype.version = version; 9 | 10 | module.exports = new PingppSDK(); 11 | 12 | var testmode = require('./testmode'); 13 | var callbacks = require('./callbacks'); 14 | var PingppError = require('./errors').Error; 15 | var mods = require('./mods'); 16 | var stash = require('./stash'); 17 | var payment_elements = require('./payment_elements'); 18 | var transfer_elements = require('./transfer_elements'); 19 | 20 | PingppSDK.prototype.createPayment = function ( 21 | chargeJSON, callback, signature, debug 22 | ) { 23 | if (typeof callback === 'function') { 24 | callbacks.userCallback = callback; 25 | } 26 | 27 | try { 28 | payment_elements.init(chargeJSON); 29 | } catch (e) { 30 | if (e instanceof PingppError) { 31 | callbacks.innerCallback('fail', 32 | callbacks.error(e.message, e.extra)); 33 | return; 34 | } else { 35 | throw e; 36 | } 37 | } 38 | 39 | if (!hasOwn.call(payment_elements, 'id')) { 40 | callbacks.innerCallback('fail', 41 | callbacks.error('invalid_charge', 'no_charge_id')); 42 | return; 43 | } 44 | if (!hasOwn.call(payment_elements, 'channel')) { 45 | callbacks.innerCallback('fail', 46 | callbacks.error('invalid_charge', 'no_channel')); 47 | return; 48 | } 49 | 50 | if (hasOwn.call(payment_elements, 'app')) { 51 | if (typeof payment_elements.app === 'string') { 52 | stash.app_id = payment_elements.app; 53 | } else if (typeof payment_elements.app === 'object' && 54 | typeof payment_elements.app.id === 'string') { 55 | stash.app_id = payment_elements.app.id; 56 | } 57 | } 58 | var channel = payment_elements.channel; 59 | if (!hasOwn.call(payment_elements, 'credential')) { 60 | callbacks.innerCallback('fail', 61 | callbacks.error('invalid_charge', 'no_credential')); 62 | return; 63 | } 64 | if (payment_elements.paid && payment_elements.actual_amount === 0) { 65 | callbacks.innerCallback('success'); 66 | return; 67 | } 68 | 69 | if (!payment_elements.credential) { 70 | callbacks.innerCallback('fail', 71 | callbacks.error('invalid_credential', 'credential_is_undefined')); 72 | return; 73 | } 74 | if (!hasOwn.call(payment_elements.credential, channel)) { 75 | callbacks.innerCallback('fail', 76 | callbacks.error('invalid_credential', 'credential_is_incorrect')); 77 | return; 78 | } 79 | if (!hasOwn.call(payment_elements, 'livemode')) { 80 | callbacks.innerCallback('fail', 81 | callbacks.error('invalid_charge', 'no_livemode_field')); 82 | return; 83 | } 84 | var channelModule = mods.getChannelModule(channel); 85 | if (typeof channelModule === 'undefined') { 86 | console.error('channel module "' + channel + '" is undefined'); 87 | callbacks.innerCallback('fail', 88 | callbacks.error('invalid_channel', 89 | 'channel module "' + channel + '" is undefined') 90 | ); 91 | return; 92 | } 93 | if (payment_elements.livemode === false) { 94 | if (hasOwn.call(channelModule, 'runTestMode')) { 95 | channelModule.runTestMode(payment_elements); 96 | } else { 97 | testmode.runTestMode(payment_elements); 98 | } 99 | return; 100 | } 101 | 102 | if (typeof signature != 'undefined') { 103 | stash.signature = signature; 104 | } 105 | if (typeof debug == 'boolean') { 106 | stash.debug = debug; 107 | } 108 | channelModule.handleCharge(payment_elements); 109 | }; 110 | 111 | PingppSDK.prototype.setAPURL = function (url) { 112 | stash.APURL = url; 113 | }; 114 | 115 | PingppSDK.prototype.setUrlReturnCallback = function (callback, channels) { 116 | if (typeof callback === 'function') { 117 | callbacks.urlReturnCallback = callback; 118 | } else { 119 | throw 'callback need to be a function'; 120 | } 121 | 122 | if (typeof channels !== 'undefined') { 123 | if (Array.isArray(channels)) { 124 | callbacks.urlReturnChannels = channels; 125 | } else { 126 | throw 'channels need to be an array'; 127 | } 128 | } 129 | }; 130 | 131 | PingppSDK.prototype.signAgreement = function (agreement, callback) { 132 | if (typeof callback === 'function') { 133 | callbacks.userAgreementCallback = callback; 134 | } 135 | 136 | var module = mods.getExtraModule('agreement'); 137 | if (typeof module === 'undefined') { 138 | console.error('module "agreement" is undefined'); 139 | callbacks.innerCallback('fail', 140 | callbacks.error('invalid_module', 141 | 'module "agreement" is undefined') 142 | ); 143 | return false; 144 | } 145 | 146 | return module.signAgreement(agreement); 147 | }; 148 | 149 | PingppSDK.prototype.createTransfer = function (transfer, callback) { 150 | if (typeof callback === "function") { 151 | callbacks.userTransferCallback = callback; 152 | } 153 | 154 | try { 155 | transfer_elements.init(transfer); 156 | } catch (e) { 157 | if (e instanceof PingppError) { 158 | callbacks.innerTransferCallback( 159 | "fail", 160 | callbacks.error(e.message, e.extra), 161 | ); 162 | return; 163 | } else { 164 | throw e; 165 | } 166 | } 167 | 168 | if (!hasOwn.call(transfer_elements, "id")) { 169 | callbacks.innerTransferCallback( 170 | "fail", 171 | callbacks.error("invalid_transfer", "no_transfer_id"), 172 | ); 173 | return; 174 | } 175 | 176 | if (!hasOwn.call(transfer_elements, "channel")) { 177 | callbacks.innerTransferCallback( 178 | "fail", 179 | callbacks.error("invalid_transfer", "no_channel"), 180 | ); 181 | return; 182 | } 183 | 184 | if (hasOwn.call(transfer_elements, "app")) { 185 | if (typeof transfer_elements.app === "string") { 186 | stash.app_id = transfer_elements.app; 187 | } else if ( 188 | typeof transfer_elements.app === "object" && 189 | typeof transfer_elements.app.id === "string" 190 | ) { 191 | stash.app_id = transfer_elements.app.id; 192 | } 193 | } 194 | 195 | const channel = transfer_elements.channel; 196 | if (!hasOwn.call(transfer_elements, "extra")) { 197 | callbacks.innerTransferCallback( 198 | "fail", 199 | callbacks.error("invalid_transfer", "no_credential"), 200 | ); 201 | return; 202 | } 203 | if (transfer_elements.status === "paid") { 204 | callbacks.innerTransferCallback("success"); 205 | return; 206 | } 207 | 208 | if (!transfer_elements.extra) { 209 | callbacks.innerTransferCallback( 210 | "fail", 211 | callbacks.error("invalid_credential", "credential_is_undefined"), 212 | ); 213 | return; 214 | } 215 | 216 | if (!hasOwn.call(transfer_elements, "livemode")) { 217 | callbacks.innerTransferCallback( 218 | "fail", 219 | callbacks.error("invalid_transfer", "no_livemode_field"), 220 | ); 221 | return; 222 | } 223 | const channelModule = mods.getTransferChannelModule(channel); 224 | if (typeof channelModule === "undefined") { 225 | console.error('transfer channel module "' + channel + '" is undefined'); 226 | callbacks.innerTransferCallback( 227 | "fail", 228 | callbacks.error( 229 | "invalid_channel", 230 | 'transfer channel module "' + channel + '" is undefined', 231 | ), 232 | ); 233 | return; 234 | } 235 | if (transfer_elements.livemode === false) { 236 | callbacks.innerTransferCallback( 237 | "fail", 238 | callbacks.error("invalid_transfer", "testmode_not_supported"), 239 | ); 240 | return; 241 | } 242 | 243 | if (typeof signature != "undefined") { 244 | stash.signature = signature; 245 | } 246 | if (typeof debug == "boolean") { 247 | stash.debug = debug; 248 | } 249 | channelModule.handleTransfer(transfer_elements); 250 | }; 251 | -------------------------------------------------------------------------------- /src/mods.js: -------------------------------------------------------------------------------- 1 | var hasOwn = {}.hasOwnProperty; 2 | var mods = {}; 3 | module.exports = mods; 4 | 5 | mods.channels = { 6 | abc_pay: require('./channels/abc_pay'), 7 | abc_pub: require('./channels/abc_pub'), 8 | alipay: require('./channels/alipay'), 9 | alipay_lite: require('./channels/alipay_lite'), 10 | alipay_pc_direct: require('./channels/alipay_pc_direct'), 11 | alipay_qr: require('./channels/alipay_qr'), 12 | alipay_qr_lakala: require('./channels/alipay_qr_lakala'), 13 | alipay_wap: require('./channels/alipay_wap'), 14 | alipay_wap_lakala: require('./channels/alipay_wap_lakala'), 15 | bfb_wap: require('./channels/bfb_wap'), 16 | cb_alipay_pc_direct: require('./channels/cb_alipay_pc_direct'), 17 | cb_alipay_wap: require('./channels/cb_alipay_wap'), 18 | cb_wx_pub: require('./channels/cb_wx_pub'), 19 | ccb_qr: require('./channels/ccb_qr'), 20 | ccb_wap: require('./channels/ccb_wap'), 21 | chinaums_alipay_pub: require('./channels/chinaums_alipay_pub'), 22 | chinaums_alipay_wap: require('./channels/chinaums_alipay_wap'), 23 | chinaums_upacp_wap: require('./channels/chinaums_upacp_wap'), 24 | chinaums_wx_pub: require('./channels/chinaums_wx_pub'), 25 | chinaums_wx_wap: require('./channels/chinaums_wx_wap'), 26 | cmb_pc_qr: require('./channels/cmb_pc_qr'), 27 | cmb_wallet: require('./channels/cmb_wallet'), 28 | cp_b2b: require('./channels/cp_b2b'), 29 | isv_lite: require('./channels/isv_lite'), 30 | isv_wap: require('./channels/isv_wap'), 31 | jdpay_wap: require('./channels/jdpay_wap'), 32 | nucc_b2b_lakala: require('./channels/nucc_b2b_lakala'), 33 | nucc_b2c_lakala: require('./channels/nucc_b2c_lakala'), 34 | pab_pc: require('./channels/pab_pc'), 35 | paypal: require('./channels/paypal'), 36 | qpay_pub: require('./channels/qpay_pub'), 37 | upacp_b2b: require('./channels/upacp_b2b'), 38 | upacp_pc: require('./channels/upacp_pc'), 39 | upacp_wap: require('./channels/upacp_wap'), 40 | wx_lite: require('./channels/wx_lite'), 41 | wx_lite_pab: require('./channels/wx_lite_pab'), 42 | wx_pub: require('./channels/wx_pub'), 43 | wx_pub_pab: require('./channels/wx_pub_pab'), 44 | wx_wap: require('./channels/wx_wap'), 45 | yeepay_wap: require('./channels/yeepay_wap'), 46 | yeepay_wx_lite: require('./channels/yeepay_wx_lite'), 47 | yeepay_wx_lite_ofl: require('./channels/yeepay_wx_lite_ofl'), 48 | yeepay_wx_pub: require('./channels/yeepay_wx_pub'), 49 | yeepay_wx_pub_ofl: require('./channels/yeepay_wx_pub_ofl') 50 | }; 51 | 52 | mods.extras = { 53 | ap: require('./channels/extras/ap'), 54 | agreement: require('./agreement') 55 | }; 56 | 57 | mods.transferChannels = { 58 | wx_pub: require('./transfer_channels/wx_pub'), 59 | } 60 | 61 | mods.getChannelModule = function(channel) { 62 | if (hasOwn.call(mods.channels, channel)) { 63 | return mods.channels[channel]; 64 | } 65 | return undefined; 66 | }; 67 | 68 | mods.getTransferChannelModule = function(channel) { 69 | if (hasOwn.call(mods.transferChannels, channel)) { 70 | return mods.transferChannels[channel]; 71 | } 72 | return undefined; 73 | }; 74 | 75 | mods.getExtraModule = function(name) { 76 | if (hasOwn.call(mods.extras, name)) { 77 | return mods.extras[name]; 78 | } 79 | return undefined; 80 | }; 81 | -------------------------------------------------------------------------------- /src/payment_elements.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by dong on 2016/12/30. 3 | */ 4 | 5 | var PingppError = require('./errors').Error; 6 | var hasOwn = {}.hasOwnProperty; 7 | 8 | module.exports = { 9 | id: null, 10 | or_id: null, 11 | channel: null, 12 | app: null, 13 | credential: {}, 14 | extra: null, 15 | livemode: null, 16 | order_no: null, 17 | time_expire: null, 18 | paid: false, 19 | status: null, 20 | actual_amount: null, 21 | 22 | init: function (charge_or_order) { 23 | var charge; 24 | if (typeof charge_or_order === 'string') { 25 | try { 26 | charge = JSON.parse(charge_or_order); 27 | } catch (err) { 28 | throw new PingppError('json_decode_fail', err); 29 | } 30 | } else { 31 | charge = charge_or_order; 32 | } 33 | 34 | if (typeof charge === 'undefined') { 35 | throw new PingppError('json_decode_fail'); 36 | } 37 | 38 | if (hasOwn.call(charge, 'object') && charge.object == 'order') { 39 | charge.or_id = charge.id; 40 | charge.order_no = charge.merchant_order_no; 41 | var charge_essentials = charge.charge_essentials; 42 | charge.channel = charge_essentials.channel; 43 | charge.credential = charge_essentials.credential; 44 | charge.extra = charge_essentials.extra; 45 | if(hasOwn.call(charge, 'charge') && charge.charge != null) { 46 | charge.id = charge.charge; 47 | } else if(hasOwn.call(charge_essentials, 'id') 48 | && charge_essentials.id != null) { 49 | charge.id = charge_essentials.id; 50 | } else if(hasOwn.call(charge, 'charges')) { 51 | for(var i = 0; i < charge.charges.data.length; i++){ 52 | if(charge.charges.data[i].channel === charge_essentials.channel) { 53 | charge.id = charge.charges.data[i].id; 54 | break; 55 | } 56 | } 57 | } 58 | } else if(hasOwn.call(charge, 'object') && charge.object == 'recharge') { 59 | charge = charge.charge; 60 | } 61 | 62 | for (var key in this) { 63 | if (hasOwn.call(charge, key)) { 64 | this[key] = charge[key]; 65 | } 66 | } 67 | return this; 68 | }, 69 | 70 | clear: function () { 71 | for (var key in this) { 72 | if (typeof this[key] !== 'function') { 73 | this[key] = null; 74 | } 75 | } 76 | } 77 | }; -------------------------------------------------------------------------------- /src/stash.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | }; 3 | -------------------------------------------------------------------------------- /src/testmode.js: -------------------------------------------------------------------------------- 1 | var utils = require('./utils'); 2 | var hasOwn = {}.hasOwnProperty; 3 | module.exports = { 4 | PINGPP_MOCK_URL: 'https://sissi.pingxx.com/mock.php', 5 | 6 | runTestMode: function (charge) { 7 | var params = { 8 | 'ch_id': charge.id, 9 | 'scheme': 'http', 10 | 'channel': charge.channel 11 | }; 12 | 13 | if (hasOwn.call(charge, 'order_no')) { 14 | params.order_no = charge.order_no; 15 | } else if (hasOwn.call(charge, 'orderNo')) { 16 | params.order_no = charge.orderNo; 17 | } 18 | if (hasOwn.call(charge, 'time_expire')) { 19 | params.time_expire = charge.time_expire; 20 | } else if (hasOwn.call(charge, 'timeExpire')) { 21 | params.time_expire = charge.timeExpire; 22 | } 23 | if (hasOwn.call(charge, 'extra')) { 24 | params.extra = encodeURIComponent(JSON.stringify(charge.extra)); 25 | } 26 | utils.redirectTo( 27 | this.PINGPP_MOCK_URL + '?' + utils.stringifyData(params), 28 | charge.channel 29 | ); 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /src/transfer_channels/wx_pub.js: -------------------------------------------------------------------------------- 1 | var callbacks = require('../callbacks'); 2 | var stash = require('../stash'); 3 | var mods = require('../mods'); 4 | var hasOwn = {}.hasOwnProperty; 5 | 6 | module.exports = { 7 | handleTransfer: function(transfer) { 8 | const credential = {}; 9 | const fields = [ 10 | 'appId', 'mchId', 'package' 11 | ]; 12 | for (let k = 0; k < fields.length; k++) { 13 | if (!hasOwn.call(transfer.extra, fields[k])) { 14 | callbacks.innerTransferCallback('fail', callbacks.error('invalid_credential', 'missing_field_' + fields[k])); 15 | console.error(fields[k]); 16 | return; 17 | } else { 18 | credential[fields[k]] = transfer.extra[fields[k]]; 19 | } 20 | } 21 | stash.jsApiParameters = credential; 22 | this.callTransfer(); 23 | }, 24 | 25 | callTransfer: function() { 26 | const self = this; 27 | const wx_jssdk = mods.getExtraModule('wx_jssdk'); 28 | if (typeof wx_jssdk !== 'undefined' && wx_jssdk.jssdkEnabled()) { 29 | wx_jssdk.callpay(); 30 | } else if (typeof WeixinJSBridge === 'undefined') { 31 | const eventCallback = function () { 32 | self.jsApiCall(); 33 | }; 34 | if (typeof document === 'undefined') { 35 | callbacks.innerTransferCallback( 36 | "fail", 37 | callbacks.error( 38 | "invalid_environment", 39 | "document_is_undefined", 40 | ), 41 | ); 42 | return; 43 | } 44 | if (document.addEventListener) { 45 | document.addEventListener('WeixinJSBridgeReady', 46 | eventCallback, false); 47 | } else if (document.attachEvent) { 48 | document.attachEvent('WeixinJSBridgeReady', eventCallback); 49 | document.attachEvent('onWeixinJSBridgeReady', eventCallback); 50 | } 51 | } else { 52 | this.jsApiCall(); 53 | } 54 | }, 55 | 56 | jsApiCall: function() { 57 | if (hasOwn.call(stash, 'jsApiParameters')) { 58 | WeixinJSBridge.invoke( 59 | 'requestMerchantTransfer', 60 | stash.jsApiParameters, 61 | function(res) { 62 | delete stash.jsApiParameters; 63 | if (res.err_msg === 'requestMerchantTransfer:ok') { 64 | callbacks.innerTransferCallback("success"); 65 | } else if (res.err_msg === 'requestMerchantTransfer:cancel') { 66 | callbacks.innerTransferCallback("cancel"); 67 | } else { 68 | callbacks.innerTransferCallback( 69 | "fail", 70 | callbacks.error("wx_result_fail", res.err_msg), 71 | ); 72 | } 73 | } 74 | ); 75 | } 76 | } 77 | }; 78 | -------------------------------------------------------------------------------- /src/transfer_elements.js: -------------------------------------------------------------------------------- 1 | var PingppError = require('./errors').Error; 2 | var hasOwn = {}.hasOwnProperty; 3 | 4 | module.exports = { 5 | id: null, 6 | channel: null, 7 | app: null, 8 | extra: null, 9 | livemode: null, 10 | order_no: null, 11 | time_expire: null, 12 | status: null, 13 | 14 | init: function (params) { 15 | let transfer; 16 | if (typeof params === 'string') { 17 | try { 18 | transfer = JSON.parse(params); 19 | } catch (err) { 20 | throw new PingppError('json_decode_fail', err); 21 | } 22 | } else { 23 | transfer = params; 24 | } 25 | 26 | if (typeof transfer === 'undefined') { 27 | throw new PingppError('json_decode_fail'); 28 | } 29 | 30 | for (var key in this) { 31 | if (hasOwn.call(transfer, key)) { 32 | this[key] = transfer[key]; 33 | } 34 | } 35 | return this; 36 | }, 37 | 38 | clear: function () { 39 | for (var key in this) { 40 | if (typeof this[key] !== 'function') { 41 | this[key] = null; 42 | } 43 | } 44 | } 45 | }; -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | var callbacks = require('./callbacks'); 2 | var hasOwn = {}.hasOwnProperty; 3 | 4 | var utils = module.exports = { 5 | 6 | /** 7 | * 对象转换成 query string 8 | * @param object data 待转对象 9 | * @param string channel 渠道 10 | * @param boolean urlencode 是否需要 urlencode 11 | * 12 | * @return string query string 13 | */ 14 | stringifyData: function (data, channel, urlencode) { 15 | if (typeof urlencode == 'undefined') { 16 | urlencode = false; 17 | } 18 | var output = []; 19 | for (var i in data) { 20 | if (!hasOwn.call(data, i) || typeof data[i] === 'function') { 21 | continue; 22 | } 23 | if (channel == 'bfb_wap' && i == 'url') { 24 | continue; 25 | } 26 | if (channel == 'yeepay_wap' && i == 'mode') { 27 | continue; 28 | } 29 | if (i == 'channel_url') { 30 | continue; 31 | } 32 | output.push(i + '=' + 33 | (urlencode ? encodeURIComponent(data[i]) : data[i])); 34 | } 35 | return output.join('&'); 36 | }, 37 | 38 | /** 39 | * 网络请求 40 | * @param string url 41 | * @param string method 请求方式, POST, GET ... 42 | * @param object requestData 请求数据 43 | * @param function successCallback 成功回调 (data, statusCode, xhr) 44 | * @param function errorCallback 错误回调 (xhr, statusCode, error) 45 | */ 46 | request: function (url, method, requestData, 47 | successCallback, errorCallback, headers) { 48 | if (typeof XMLHttpRequest === 'undefined') { 49 | console.log('Function XMLHttpRequest is undefined.'); 50 | return; 51 | } 52 | var xhr = new XMLHttpRequest(); 53 | if (typeof xhr.timeout !== 'undefined') { 54 | xhr.timeout = 6000; 55 | } 56 | method = method.toUpperCase(); 57 | 58 | if (method === 'GET' && typeof requestData === 'object' && requestData) { 59 | url += '?' + utils.stringifyData(requestData, '', true); 60 | } 61 | xhr.open(method, url, true); 62 | if (typeof headers !== 'undefined') { 63 | for (var k in headers) { 64 | if (hasOwn.call(headers, k)) { 65 | xhr.setRequestHeader(k, headers[k]); 66 | } 67 | } 68 | } 69 | if (method === 'POST') { 70 | xhr.setRequestHeader('Content-type', 'application/json; charset=utf-8'); 71 | xhr.send(JSON.stringify(requestData)); 72 | } else { 73 | xhr.send(); 74 | } 75 | if (typeof successCallback == 'undefined') { 76 | successCallback = function () {}; 77 | } 78 | if (typeof errorCallback == 'undefined') { 79 | errorCallback = function () {}; 80 | } 81 | xhr.onreadystatechange = function () { 82 | if (xhr.readyState == 4) { 83 | successCallback(xhr.responseText, xhr.status, xhr); 84 | } 85 | }; 86 | xhr.onerror = function (e) { 87 | errorCallback(xhr, 0, e); 88 | }; 89 | }, 90 | 91 | /** 92 | * 表单提交 93 | * @param string url 94 | * @param string method 请求方式, POST, GET ... 95 | * @param object params 请求数据 96 | */ 97 | formSubmit: function (url, method, params) { 98 | if (typeof window === 'undefined') { 99 | console.log('Not a browser, form submit url: ' + url); 100 | return; 101 | } 102 | var form = document.createElement('form'); 103 | form.setAttribute('method', method); 104 | form.setAttribute('action', url); 105 | 106 | for (var key in params) { 107 | if (hasOwn.call(params, key)) { 108 | var hiddenField = document.createElement('input'); 109 | hiddenField.setAttribute('type', 'hidden'); 110 | hiddenField.setAttribute('name', key); 111 | hiddenField.setAttribute('value', params[key]); 112 | form.appendChild(hiddenField); 113 | } 114 | } 115 | 116 | document.body.appendChild(form); 117 | form.submit(); 118 | }, 119 | 120 | randomString: function (length) { 121 | if (typeof length == 'undefined') { 122 | length = 32; 123 | } 124 | var chars = 125 | 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; 126 | var maxPos = chars.length; 127 | var str = ''; 128 | for (var i = 0; i < length; i++) { 129 | str += chars.charAt(Math.floor(Math.random() * maxPos)); 130 | } 131 | 132 | return str; 133 | }, 134 | 135 | redirectTo: function (url, channel) { 136 | if (callbacks.shouldReturnUrlByCallback(channel)) { 137 | callbacks.triggerUrlReturnCallback(null, url); 138 | return; 139 | } 140 | if (typeof window === 'undefined') { 141 | console.log('Not a browser, redirect url: ' + url); 142 | return; 143 | } 144 | window.location.href = url; 145 | }, 146 | 147 | inWeixin: function () { 148 | if (typeof navigator === 'undefined') { 149 | return false; 150 | } 151 | var ua = navigator.userAgent.toLowerCase(); 152 | return ua.indexOf('micromessenger') !== -1; 153 | }, 154 | 155 | inAlipay: function () { 156 | if (typeof navigator === 'undefined') { 157 | return false; 158 | } 159 | var ua = navigator.userAgent.toLowerCase(); 160 | return ua.indexOf('alipayclient') !== -1; 161 | }, 162 | 163 | /*global wx*/ 164 | inWxLite: function() { 165 | if(typeof wx === 'undefined') { 166 | return false; 167 | } 168 | 169 | return wx.miniProgram || wx.requestPayment; 170 | }, 171 | 172 | /*global my*/ 173 | inAlipayLite: function() { 174 | if(typeof my === 'undefined') { 175 | return false; 176 | } 177 | 178 | return my.tradePay; 179 | }, 180 | 181 | documentReady: function (fn) { 182 | if (typeof document === 'undefined') { 183 | fn(); 184 | return; 185 | } 186 | if (document.readyState != 'loading') { 187 | fn(); 188 | } else { 189 | document.addEventListener('DOMContentLoaded', fn); 190 | } 191 | }, 192 | 193 | loadUrlJs: function (sid, jsurl, callback) { 194 | var nodeHead = document.getElementsByTagName('head')[0]; 195 | var nodeScript = null; 196 | if (document.getElementById(sid) == null) { 197 | nodeScript = document.createElement('script'); 198 | nodeScript.setAttribute('type', 'text/javascript'); 199 | nodeScript.setAttribute('src', jsurl); 200 | nodeScript.setAttribute('id', sid); 201 | nodeScript.async = true; 202 | if (callback != null) { 203 | nodeScript.onload = nodeScript.onreadystatechange = function () { 204 | if (nodeScript.ready) { 205 | return false; 206 | } 207 | 208 | if (!nodeScript.readyState || nodeScript.readyState == 'loaded' 209 | || nodeScript.readyState == 'complete') { 210 | nodeScript.ready = true; 211 | callback(); 212 | } 213 | }; 214 | } 215 | nodeHead.appendChild(nodeScript); 216 | } else { 217 | if (callback != null) { 218 | callback(); 219 | } 220 | } 221 | }, 222 | deviceDetectorMobile: function () { 223 | return /Mobi/.test(window.navigator.userAgent); 224 | }, 225 | }; 226 | -------------------------------------------------------------------------------- /src/version.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | v: '2.4.0' 3 | }; 4 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | /*eslint quotes: ["off"]*/ 2 | /*eslint max-len: ["off"]*/ 3 | 4 | module.exports = { 5 | pingpp: undefined, 6 | 7 | run: function() { 8 | const version = require('../package.json').version; 9 | this.pingpp = require('../src/main.js'); 10 | if (version !== this.pingpp.version) { 11 | console.error('Version number does not match.'); 12 | } else { 13 | console.log(`Version: ${this.pingpp.version}`); 14 | } 15 | 16 | this.testCharge(); 17 | this.testChargeCcbWap(); 18 | this.testAgreement(); 19 | this.testTransfer() 20 | }, 21 | 22 | testTransfer: function () { 23 | console.log('running transfer test...'); 24 | 25 | const transfer = { 26 | "id": "tr_131250327649096837120006", 27 | "object": "transfer", 28 | "type": "b2c", 29 | "created": 1743068187, 30 | "time_transferred": null, 31 | "livemode": true, 32 | "status": "wait_user_confirm", 33 | "app": "app_1Gqj58ynP0mHeX1q", 34 | "channel": "wx_pub", 35 | "order_no": "912111743068187", 36 | "batch_no": null, 37 | "amount": 10, 38 | "amount_settle": 10, 39 | "currency": "cny", 40 | "recipient": "otdLiw0bx_HJUALDp_dzAAjp7BCA", 41 | "description": "转账测试", 42 | "transaction_no": "1330000139344692503270012303775666", 43 | "failure_msg": null, 44 | "extra": { 45 | "scene_id": "1005", 46 | "product_code": "fund-app", 47 | "scene_report_info": [ 48 | { 49 | "info_type": "岗位类型", 50 | "info_content": "测试" 51 | }, 52 | { 53 | "info_type": "报酬说明", 54 | "info_content": "测试" 55 | } 56 | ], 57 | "package": "ABBQO+oYAAABAAAAAADBGUx8UBAabcdHHhzlZxAAAADnGabcZahT9IkJjn90+abcRXA0BwkohpO9+F1BLQuBl2pCXphJtQK18bS/ShfGsIUmr1UTByVyMKCC/0/aUmIbP9y7Q8s7qutM1gFiMDRKQbtU2ks=", 58 | "mchId": "1250015000", 59 | "appId": "wx9cfac8008001600" 60 | }, 61 | "metadata": {} 62 | } 63 | 64 | const ret = this.pingpp.createTransfer(transfer, (result, error) => { 65 | console.log(result); 66 | console.log(error); 67 | }); 68 | 69 | console.log(ret); 70 | }, 71 | 72 | testCharge: function () { 73 | console.log('running charge test...'); 74 | 75 | this.pingpp.setUrlReturnCallback(function (err, url) { 76 | console.log("UrlReturnCallback: " + url); 77 | }); 78 | 79 | var charge = `{ 80 | "id": "ch_jD0mL8L88i9Sub9OG0qTiTOS", 81 | "object": "charge", 82 | "created": 1512994590, 83 | "livemode": true, 84 | "paid": false, 85 | "refunded": false, 86 | "reversed": false, 87 | "app": "app_1Gqj58ynP0mHeX1q", 88 | "channel": "alipay_pc_direct", 89 | "order_no": "201700211512994583", 90 | "client_ip": "192.168.23.32", 91 | "amount": 100, 92 | "amount_settle": 99, 93 | "currency": "cny", 94 | "subject": "测试订单", 95 | "body": "订单详情", 96 | "extra": { 97 | "success_url": "https://example.com/success", 98 | "qr_pay_mode": 4, 99 | "qrcode_width": 300 100 | }, 101 | "time_paid": null, 102 | "time_expire": 1513080990, 103 | "time_settle": null, 104 | "transaction_no": null, 105 | "refunds": { 106 | "object": "list", 107 | "url": "/v1/charges/ch_jD0mL8L88i9Sub9OG0qTiTOS/refunds", 108 | "has_more": false, 109 | "data": [] 110 | }, 111 | "amount_refunded": 0, 112 | "failure_code": null, 113 | "failure_msg": null, 114 | "metadata": {}, 115 | "credential": { 116 | "object": "credential", 117 | "alipay_pc_direct": { 118 | "app_id": "2016110302520983", 119 | "method": "alipay.trade.page.pay", 120 | "format": "JSON", 121 | "charset": "utf-8", 122 | "sign_type": "RSA2", 123 | "timestamp": "2017-12-11 20:16:30", 124 | "version": "1.0", 125 | "biz_content": "{\\"body\\":\\"订单详情\\",\\"subject\\":\\"测试订单\\",\\"out_trade_no\\":\\"201700211512994583\\",\\"total_amount\\":1,\\"product_code\\":\\"FAST_INSTANT_TRADE_PAY\\",\\"timeout_express\\":\\"1440m\\",\\"qr_pay_mode\\":4,\\"qrcode_width\\":300}", 126 | "notify_url": "https://notify.pingxx.com/notify/charges/ch_jD0mL8L88i9Sub9OG0qTiTOS", 127 | "return_url": "https://example.com/success", 128 | "sign": "qj7mZXBmp7s8aoWlfruqbJWNZc/Qmzrzo1BFhYvbQL6dJskc3NX7oScZuwJIoBhts/mgO77+ziYceI1XANJBhXhFzwJQS0vgEBpwzxAEY008FSbf8Rolfck6C2lyXH+4uhjlk5bzwBAMa2VudR0zcfK1/rUiHVNuG/YHhQIietyyG1SVL/YnMhwo+b7cDLn87pj6/tYRxBNob9cLRMZLQ9JOgHb2JRuRxDpiIpfSlBQMMnoC2TvkUkrLGjhdSsUXd1QqTDbT28+tsMtPMBIjw4Lmgpw6Qr/Y39MtVTOUiSIHdwnG37+8GVML6zfP9v6KNYwH62GNIU0PfpsMzv4p+w==", 129 | "channel_url": "https://openapi.alipay.com/gateway.do" 130 | } 131 | }, 132 | "description": null 133 | }`; 134 | 135 | this.pingpp.createPayment(charge, function(result, error){ 136 | console.log(result); 137 | console.log(error); 138 | }); 139 | }, 140 | 141 | testAgreement: function () { 142 | console.log('running agreement test...'); 143 | 144 | var agreement = { 145 | "id": "agr_19EFuyiLcdTshr", 146 | "object": "agreement", 147 | "livemode": true, 148 | "app": "app_1Gqj58ynP0mHeX1q", 149 | "created": 1545882366, 150 | "channel": "wx", 151 | "contract_no": "912111545882234", 152 | "contract_id": null, 153 | "credential": { 154 | "object": "credential", 155 | "wx": { 156 | "credential": "https://api.mch.weixin.qq.com/papay/entrustweb?appid=wx87cae2333f5068e1&mch_id=1300233301&plan_id=123611&contract_code=912111545882234&request_serial=912111545882234&contract_display_account=test¬ify_url=https%3A%2F%2Fnotify.pingxx.com%2Fnotify%2Fagreements%2Fagr_19EFuyiLcdTsht&version=1.0×tamp=1545882366&sign=11E5F8C28FE1CE78CF7FEEC1C502EC33" 157 | } 158 | }, 159 | "status": "created", 160 | "time_succeeded": null, 161 | "time_canceled": null, 162 | "failure_code": null, 163 | "failure_msg": null, 164 | "extra": { 165 | "plan_id": "123611", 166 | "request_serial": "912111545882234", 167 | "display_account": "test" 168 | }, 169 | "metadata": {}, 170 | "source": null 171 | }; 172 | 173 | var ret = this.pingpp.signAgreement(agreement, function (result, error) { 174 | console.log(result); 175 | console.log(error); 176 | }); 177 | 178 | console.log(ret); 179 | }, 180 | 181 | testChargeCcbWap: function () { 182 | console.log('running charge ccb_wap test...'); 183 | 184 | this.pingpp.setUrlReturnCallback(function (err, url) { 185 | console.log("UrlReturnCallback: " + url); 186 | }, ['ccb_wap']); 187 | 188 | var charge = `{ 189 | "id": "ch_101200415434992752640001", 190 | "object": "charge", 191 | "created": 1586922479, 192 | "livemode": true, 193 | "paid": false, 194 | "refunded": false, 195 | "reversed": false, 196 | "app": "app_1Gqj58ynP0mHeX1q", 197 | "channel": "ccb_wap", 198 | "order_no": "202002060026", 199 | "client_ip": "127.0.0.1", 200 | "amount": 1, 201 | "amount_settle": 1, 202 | "currency": "cny", 203 | "subject": "测试订单", 204 | "body": "订单详情", 205 | "extra": { 206 | "pos_id": "044507853" 207 | }, 208 | "time_paid": null, 209 | "time_expire": 1587008879, 210 | "time_settle": null, 211 | "transaction_no": null, 212 | "refunds": { 213 | "object": "list", 214 | "url": "/v1/charges/ch_101200415434992752640001/refunds", 215 | "has_more": false, 216 | "data": [] 217 | }, 218 | "amount_refunded": 0, 219 | "failure_code": null, 220 | "failure_msg": null, 221 | "metadata": {}, 222 | "credential": { 223 | "object": "credential", 224 | "ccb_wap": { 225 | "orderinfo": "MERCHANTID=105000056222895&POSID=044507853&BRANCHID=340000000&ORDERID=202002060026&PAYMENT=0.01&CURCODE=01&TXCODE=520100&REMARK1=ch_101200415434992752640001&TIMEOUT=20200416114759&TYPE=1&GATEWAY=UnionPay&THIRDAPPINFO=comccbpay000000000000000ccb&MAC=a4b3dbf79a7b5ca60da2997103ab6ce9" 226 | } 227 | }, 228 | "description": null 229 | }`; 230 | 231 | this.pingpp.createPayment(charge, function(result, error){ 232 | console.log(result); 233 | console.log(error); 234 | }); 235 | }, 236 | }; 237 | --------------------------------------------------------------------------------