使用 ngrok 和 Express(节点)测试 Sandbox Webhooks
在这个例子中,我们将讨论在沙盒中测试 webhook 通知,使用 ngrok 为我们的 Node HTTP 侦听器提供隧道,在 localhost 上运行到 Internet。对于此示例,我们将使用 Node 为付款事件设置通知 webhook(例如正在进行付款),然后设置服务器以侦听来自 webhook 事件的传入 HTTP POST 消息。
我们将在此处执行以下几个步骤来实现此目的:
- 设置一个简单的服务器来监听来自 webhooks 的传入 POST 流量,这将是来自 PayPal 的通知,并开始侦听 localhost。
- 然后使用 ngrok 提供从 localhost 到 Internet 的隧道,以便 PayPal 可以发布通知。
- 最后,将我们的应用程序(基于提供的凭据)订阅到我们要跟踪的 webhook 事件,从步骤 2 提供公共 ngrok URI。
创建 Webhooks 监听器
我们需要做的第一件事就是创建监听器。我们开始使用监听器的原因是因为我们需要 ngrok 实时 URL 在创建或更新时提供给 webhook。
var bodyParser = require('body-parser'),
http = require('http'),
app = require('express')();
app.use(bodyParser.json());
app.post('/', function(req, res){
console.log(JSON.stringify(req.body));
});
//create server
http.createServer(app).listen(3001, function () {
console.log('Server started: Listening on port 3001');
});
我们的听众是使用 Express 的简单路线。我们监听任何传入的 POST 流量,然后将 POST 主体吐出到控制台。我们可以使用它来做任何我们喜欢的听众。
当我们在最后创建 HTTP 服务器时,我们将其设置为侦听 localhost 端口 3001.现在运行该脚本以开始侦听流量。
使用 ngrok 将监听器暴露给 Internet
在 localhost:3001 上设置监听器,我们的下一个工作是将该脚本暴露给互联网,以便可以将流量发送给它,这是 ngrok 的工作。
从终端窗口运行以下命令:
ngrok http 3001
这将启动在端口 3001 上为 localhost 提供实时隧道的过程,并在运行后提供以下信息:
http://i.stack.imgur.com/UVwad.jpg
我们可以看到,我们可以用来将 PayPal webhook 指向我们在 localhost 上运行的监听器的实时地址是 http(s)://055b3480.ngrok.io
。这就是我们设置监听器所需要知道的全部内容。
订阅通知
我们的最后一步是为我们的应用程序创建 webhooks,当我们的应用程序上的付款,退款等发生某些事件时,它会创建通知。我们只需创建一次这些 webhook 就可以将它们绑定到应用程序,因此每次要使用它们时都不需要运行它们。
首先,我们通过添加 PayPal Node SDK 的要求,创建应用程序的客户端 ID /机密,然后为沙箱配置环境来设置 PayPal 环境。
var paypal = require('paypal-rest-sdk');
var clientId = 'YOUR APPLICATION CLIENT ID';
var secret = 'YOUR APPLICATION SECRET';
paypal.configure({
'mode': 'sandbox', //sandbox or live
'client_id': clientId,
'client_secret': secret
});
接下来,我们为 webhooks 设置 JSON 结构。webhooks
包含两条信息,即应该发送所有 webhook 事件的 url
,以及我们想要订阅的 event_types
。
对于此示例,url
设置为我们的 ngrok 实时 URL,我们正在侦听的事件是完成或拒绝付款的情况。
有关潜在事件的完整列表,请参阅 https://developer.paypal.com/docs/integration/direct/rest-webhooks-overview/#event-type-support 。
最后,我们将 webhooks
对象传递给调用以创建 webhooks,notification.webhook.create
。如果成功,PayPal 将向我们指定的端点发送通知,该端点在 localhost 上运行。
var webhooks = {
"url": "https://436e4d13.ngrok.io",
"event_types": [{
"name": "PAYMENT.SALE.COMPLETED"
},{
"name": "PAYMENT.SALE.DENIED"
}
]};
paypal.notification.webhook.create(webhooks, function (err, webhook) {
if (err) {
console.log(err.response);
throw error;
} else {
console.log("Create webhook Response");
console.log(webhook);
}
});
一旦我们使用这些应用程序凭据发出付款,有关付款状态的信息将发送到我们设置的端点。
PayPal 作为通知发送的 POST 正文的示例可能如下所示,这是在成功支付 PayPal 后发送的:
{
"id": "WH-9FE9644311463722U-6TR22899JY792883B",
"create_time": "2016-04-20T16:51:12Z",
"resource_type": "sale",
"event_type": "PAYMENT.SALE.COMPLETED",
"summary": "Payment completed for $ 7.47 USD",
"resource": {
"id": "18169707V5310210W",
"state": "completed",
"amount": {
"total": "7.47",
"currency": "USD",
"details": {
"subtotal": "7.47"
}
},
"payment_mode": "INSTANT_TRANSFER",
"protection_eligibility": "ELIGIBLE",
"protection_eligibility_type": "ITEM_NOT_RECEIVED_ELIGIBLE,UNAUTHORIZED_PAYMENT_ELIGIBLE",
"transaction_fee": {
"value": "0.52",
"currency": "USD"
},
"invoice_number": "",
"custom": "",
"parent_payment": "PAY-809936371M327284GK4L3FHA",
"create_time": "2016-04-20T16:47:36Z",
"update_time": "2016-04-20T16:50:07Z",
"links": [
{
"href": "https:\/\/api.sandbox.paypal.com\/v1\/payments\/sale\/18169707V5310210W",
"rel": "self",
"method": "GET"
},
{
"href": "https:\/\/api.sandbox.paypal.com\/v1\/payments\/sale\/18169707V5310210W\/refund",
"rel": "refund",
"method": "POST"
},
{
"href": "https:\/\/api.sandbox.paypal.com\/v1\/payments\/payment\/PAY-809936371M327284GK4L3FHA",
"rel": "parent_payment",
"method": "GET"
}
]
},
"links": [
{
"href": "https:\/\/api.sandbox.paypal.com\/v1\/notifications\/webhooks-events\/WH-9FE9644311463722U-6TR22899JY792883B",
"rel": "self",
"method": "GET"
},
{
"href": "https:\/\/api.sandbox.paypal.com\/v1\/notifications\/webhooks-events\/WH-9FE9644311463722U-6TR22899JY792883B\/resend",
"rel": "resend",
"method": "POST"
}
]
}