1.发展想法
我们总体的需求是让nodejs应用能够访问支付宝,完成用户支付。 具体流程为:
用户在支付宝页面进行支付并完成支付。 支付宝检查用户完成支付后,向商户应用发送POST请求作为支付完成的异步反弹。 商户应用验证反弹信息后,更改订单状态。 用户返回商家应用程序并刷新订单。 界面显示订单已支付二、初步方案
我们以Koa为例,简单演示一下接入支付宝的具体流程。 首先,安装 Koa 本身和所需的中间件:
npm install koa koa-router koa-static koa-bodyparser -S
然后需要在阿里巴巴官方提供的nodejs上安装支付宝SDK:
npm install alipay-node-sdk -S
当所有开发依赖计划完成后,我们就可以直接申请应用,也可以利用支付宝开放平台上的沙箱环境来模拟真实的应用。 这里我们使用沙盒环境进行开发演示。 您需要记住沙盒界面中的APPID:
同时点击下面的RSA2密钥,下载密钥生成工具,分别生成公钥和私钥。 我们需要记录生成的应用程序公钥并将其存储在 private-key.pem 文件中; 然后在页面填写“申请私钥”,从而生成支付宝私钥郑州支付宝小程序开发网站,并将私钥记录到public-key.pem文件中,前期规划工作就完成了。 如果您不清楚以上流程,请参考。
我们来整理一下文件,把秘钥文件放在一起,这样前期工作就完成了:
.
├── package.json
├── package-lock.json
├── serve.js // 主服务
└── static
├── index.html // 客户端
└── pem // 密钥存放文件夹
├── private-key.pem
└── public-key.pem
3.应用部署 3.1 alipay-node-sdk的使用
当用户点击付款信函按钮时,我们服务器上的路由条件将被触发。 在这个路由中,我们的服务器主动向支付宝服务器发送请求。 请求中携带支付信息(如订单号、产品价格)。 等),还携带公钥信息。 当支付宝服务器收到请求时,会返回一个支付URL到我们的服务器。 然后我们的服务器将URL信息转发到后端页面,后端页面完成跳转逻辑。
使用alipay-node-sdk简化了我们的服务器向支付宝服务器发送请求信息的过程。 它会处理必要的参数和加密信息,我们只需要传入业务参数即可。
创建SDK实例
当我们引入alipay-node-sdk时,首先要实例化它并设置全局参数:
const AlipaySdk = require('alipay-sdk').default;
const alipaySdk = new AlipaySdk({
appId: '2016**********710', // 之前我们所记录的沙箱环境的 sdk
privateKey: fs.readFileSync('./static/pem/private-key.pem', 'ascii'), // 传入私钥
gateway: "https://openapi.alipaydev.com/gateway.do" // 沙箱环境的请求网关与正式环境不一样,需要在此更改,如果是使用正式环境则去掉此处的设置
});
alipaySdk.exec()
alipaySdk.exec()方法可以帮助我们轻松发送业务请求。 我们可以在支付API文档中查看所有业务请求的列表。 我们发送统一收单交易关闭socket(alipay.trade.close)为例:
const result = await alipaySdk.exec('alipay.trade.close', {
notifyUrl: 'http://notify_url',
appAuthToken: '',
// 通过 bizContent 传递请求参数
bizContent: {
tradeNo: '',
outTradeNo: '',
operatorId: '',
},
});
// 从官方文档看到,result 包含 tradeNo、outTradeNo 2 个 key
console.log('tradeNo: %s, outTradeNo: %s', result.tradeNo, result.outTradeNo);
这是alipay-sdk-nodejs官方提供的demo
这导致我们需要连接两个套接字:
支付宝表单数据。 添加字段()
如果我们按照上面的方法请求alipay.trade.wap.pay和alipay.trade.page.pay这两个socket,会返回错误信息。 由于这两个套接字都属于页面类型套接字,因此页面类型套接字默认返回的数据是html代码片段。 对于这种类型的socket,我们需要创建一个FormData来请求,而不是直接使用alipaySdk.exec()传入业务参数。
Sdk提供了一个AlipayFormData方便我们创建,这里我们以alipay.trade.page.pay套接字为例:
// TypeScript
// import AlipayFormData from 'alipay-sdk/lib/form';
// js
const AlipayFormData = require('alipay-sdk/lib/form').default
const formData = new AlipayFormData();
// 调用 setMethod 并传入 get,会返回可以跳转到支付页面的 url,否则返回的是一个表单的 html 片段
formData.setMethod('get');
formData.addField('notifyUrl', 'http://www.com/notify'); // 当支付完成后,支付宝主动向我们的服务器发送回调的地址
formData.addField('returnUrl', 'http://www.com/return'); // 当支付完成后,当前页面跳转的地址
formData.addField('bizContent', {
outTradeNo: 'out_trade_no',
productCode: 'FAST_INSTANT_TRADE_PAY',
totalAmount: '0.01',
subject: '商品',
body: '商品详情',
});
const result = await alipaySdk.exec(
'alipay.trade.page.pay',
{},
{ formData: formData },
);
// result 为可以跳转到支付链接的 url
console.log(result);
这里要非常小心。 用户完成支付后,支付宝会向我们的服务器发送POST方法的异步反弹。 这个反弹地址必须在内网内可以访问,也就是说我们必须在线上开发这个流程。
3.2演示
介绍完了alipay-node-sdk的使用,我们接着用一个完整的例子来进行一次完整的演示,因为已经演示了如何请求alipay.trade.page.pay(统一订单获取和支付页面接口),那么郑州支付宝小程序开发网站,我将演示如何请求alipay.trade.wap.pay(移动网站支付套接字2.0)以允许用户进行移动支付:
注意,项目必须在线开发! 否则只会跳转到支付宝界面,收不到支付宝的异步反弹!
总体目录:
├── package.json
├── package-lock.json
├── serve.js
└── static
├── index.html
└── pem
├── private-key.pem
└── public-key.pem
服务.js
const Koa = require('koa')
const Router = require('koa-router')
const static = require('koa-static')
const path = require('path')
const fs = require('fs')
const AlipaySdk = require('alipay-sdk').default;
const AlipayFormData = require('alipay-sdk/lib/form').default
const bodyParser = require('koa-bodyparser')
const app = new Koa()
const router = new Router()
const staticPath = './static'
app.use(static(
path.join(__dirname, staticPath)
))
app.use(bodyParser())
router.get('/pay', async (ctx, next) => {
const alipaySdk = new AlipaySdk({
appId: '20161*******6710',
privateKey: fs.readFileSync('./static/pem/private-key.pem', 'ascii'),
gateway: "https://openapi.alipaydev.com/gateway.do"
});
const formData = new AlipayFormData()
formData.setMethod("get")
formData.addField("notifyUrl", "http://online_serve_url/paycallback") // 回调地址必须为当前服务的线上地址!
formData.addField("returnUrl", "http://online_serve_url/success")
formData.addField("bizContent", {
body: "测试商品",
subject: "女装",
outTradeNo: new Date().valueOf(),
totalAmount: "88.88",
quitUrl: "http://www.taobao.com/product/113714.html",
productCode: "QUICK_WAP_WAY"
})
const result = await alipaySdk.exec("alipay.trade.wap.pay", {}, {
formData: formData,
validateSign: true
})
ctx.body = result
})
router.post('/paycallback', async (ctx, next) => {
let postData = ctx.request.body;
console.log("触发付款");
if (postData.trade_status === "TRADE_SUCCESS") {
let data = ctx.request.body // 订单信息
// ========= 由请求体内的订单信息,在这里进行数据库中订单状态的更改 ============
console.log("支付完成!");
}
})
router.get('/success', async (ctx, next) => {
ctx.body = "支付成功"
})
app.use(router.routes())
app.listen(9090)
索引.html:
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="https://cdn.bootcss.com/axios/0.19.0-beta.1/axios.min.js"></script>
<script src="https://gw.alipayobjects.com/as/g/h5-lib/alipayjsapi/3.1.1/alipayjsapi.min.js"></script>
<script>
window.onload = function () {
let oPay = document.querySelector("#pay")
oPay.addEventListener('click', function () {
axios.get('http://47.106.226.190:9090/pay').then(res => {
window.open(res.data);
})
})
}
</script>
</head>
<body>
<button id="pay">创建付款</button>
<div id="form"></div>
</body>
</html>
PS:收到支付宝异步退回邮件后,需要验证异步退回邮件的签名,确保该退回邮件是支付宝发送的。 这还没有研究过。 研究完成后我们将对其进行更新。