新西兰电商网站开发公司|我们的后端工程师David总结的独立电商购物网站整合支付接口的经验,如何让大陆的买家在你的网站上通过微信支付和支付宝付款

大家好,我是新西兰软件开发公司local fern的程序员David,最近因工作需要,有机会接触到 Stripe 的工作流程,事情很简单,对于优秀的服务,我们应该付出使用他们的成本(这样他们可以继续提供优质的服务),对于商户来说收钱就是一个比较有意思的部分了,鉴于大多数网友都是付钱,本文决定分享一下 Stripe 整合支付宝来收钱的方法,且本文不是网上很多出现的那种引用 checkout.js 的过期的方法(许多人都互相转来转去,看了一圈下来都是这个),而是使用 Stripe.js 来完成。
 

Stripe 目前收款方式有两种,简单来说,我们分为 Easy 难度和 Hard 难度,前者只支持信用卡,储蓄卡和 Apple Pay,而后者则支持多种支付方式,最终的效果图如下:


Easy 模式——使用 「Checkout」
Easy 模式即使用他们写好的页面,被称为「Checkout」,对于商户来说需要在后台定义好产品(Products),生成 sku 后写一个按钮出发脚本自动跳转过去,页面上需要写的内容如下:
<!-- Load Stripe.js on your website. -->
<script src="https://js.stripe.com/v3"></script>
<!-- Create a button that your customers click to complete their purchase. Customize the styling to suit your branding. -->
<button
style="background-color:#6772E5;color:#FFF;padding:8px 12px;border:0;border-radius:4px;font-size:1em"
id="checkout-button-sku_xxxxxxxxxxx"
role="link"
>
Checkout
</button>
<div id="error-message"></div>
<script>
(function() {
var stripe = Stripe('pk_test_xxxxxxxxxxxx');
var checkoutButton = document.getElementById('checkout-button-sku_G40GQYkIX4a8c4');
checkoutButton.addEventListener('click', function () {
// When the customer clicks on the button, redirect
// them to Checkout.
stripe.redirectToCheckout({
items: [{sku: 'sku_xxxxxxxxxxx', quantity: 1}],
// Do not rely on the redirect to the successUrl for fulfilling
// purchases, customers may not always reach the success_url after
// a successful payment.
// Instead use one of the strategies described in
// https://stripe.com/docs/paymen ... lment
successUrl: 'https://xxx.xxx.xx/success',
cancelUrl: 'https://xxx.xxx.xx/canceled',
})
.then(function (result) {
if (result.error) {
// If `redirectToCheckout` fails due to a browser or network
// error, display the localized error message to your customer.
var displayError = document.getElementById('error-message');
displayError.textContent = result.error.message;
}
});
});
})();
</script>


这样在用户点了按钮之后就会出现一个 Stripe 的支付页面:


这样就可以用了,用户在付款完成之后就会跳转回到 successUrl,同时 Stripe 可以给你预先定义好的接口(WebHook)发一个 POST 请求告知,大致逻辑如下(其实官方有示范):
\Stripe\Stripe::setApiKey('sk_test_xxxxxxxxxxxxxx');

// You can find your endpoint's secret in your webhook settings
$endpoint_secret = 'whsec_xxxxxxxxxxxxxxx';

$payload = @file_get_contents('php://input');
$sig_header = $_SERVER['HTTP_STRIPE_SIGNATURE'];
$event = null;

try {
$event = \Stripe\Webhook::constructEvent(
$payload, $sig_header, $endpoint_secret
);
} catch(\UnexpectedValueException $e) {
// Invalid payload
http_response_code(400);
exit();
} catch(\Stripe\Exception\SignatureVerificationException $e) {
// Invalid signature
http_response_code(400);
exit();
}

// Handle the checkout.session.completed event
if ($event->type == 'checkout.session.completed') {
$session = $event->data->object;
// 授权用户
$target_customer = \Stripe\Customer::retrieve($session['customer']);
$target_email = $target_customer['email'];
// 然后这里自己根据 email 找到对应用户完成接下来的步骤,比如把文件通过邮件发给用户,给用户头像加个 Buff 啥的~
}



这样就可以获取到用户的信息并且给用户提供/升级服务了,很方便是不是?
不过呢,「Checkout」只支持卡和 Apple Pay,对于喜欢见到付钱就想扫一扫的用户来说并不友好,所以我们需要使用一些别的方法。
Hard 模式——使用「STRIPE ELEMENTS」
为了照顾没有信用卡,遇见码就开始掏手机准备打开或绿或蓝应用准备开始扫一扫的用户来说,我们需要加入支付宝的支持功能。
首先确认你的账户中 Alipay 是连接上并且处于激活状态的,没有这一点等于没戏(也就不用继续往下看了)。


如果你的 Stripe 已经连接上了支付宝,接下来我们就可以开始整合了。
首先我们明白一下对于商户来说,逻辑是怎么样的:


首先由于 Stripe 并不是原生支持支付宝,所以所有这种非信用卡交易都被挂了称为「Source」的东西下,可以理解为一个插件或者一个临时的钱包,以下一段是具体的逻辑,请仔细阅读:
当用户需要付款的时候,用户会先通过 JS 创建一个 「Source」对象,并指定类型为「Alipay」,这个时候 Stripe.js 会带领用户去支付宝的付款页面进行支付,如果付款成功了,那么这个「Source」的状态会从 charge.pending 变成 source.chargeable ,可以理解为用户给临时钱包付了钱,在有了这个状态之后我们可以调用 Stripe 对这个 Source 扣款(Charge),把临时钱包的钱扣到自己 Stripe 账户上,然后就完成了付款的过程。
用户逻辑
我们先来看用户的逻辑部分:
用户的逻辑是,在对应的购买页面上应该有一个 Button,上面写上「立即购买」,这样用户只要一摸那个按钮,就可以看到支付宝的付款页面了,为了满足这个需要,我们需要这么做,在对应的页面上放个 Button:
<button id="checkout-button">
立即购买
</button>


然后引用 stripe.js 并写一点 JS 来完成接下来的事情:
<script src="https://js.stripe.com/v3/"></script>
<script type="text/javascript">
(function() {
var stripe = Stripe('pk_xxxxxxxxxxxxxx');
var checkout-button = document.getElementById('checkout-button');
checkout-button.addEventListener('click', function () {
stripe.createSource({
type: 'alipay',
amount: 1988,
currency: 'hkd',
// 这里你需要渲染出一些用户的信息,不然后期没法知道是谁在付钱
owner: {
email: '{$user_email}',
},
redirect: {
return_url: 'https://xxx.xxx.xx/buy',
},
}).then(function(result) {
window.location.replace(result.source.redirect.url);
});
});
})();
</script>

其中,owner 和 owner 下的 email 建议填写,不然付款后可能不好找到究竟是哪个用户付了钱,如果正巧你们不用 email 来标识用户,那也可以写点别的,对于 owner 来说有以下字段可供选择:
  
"owner": {
"address": null,
"email": "[email protected]",
"name": null,
"phone": null,
"verified_address": null,
"verified_email": null,
"verified_name": null,
"verified_phone": null
},

此外,如果你还希望在 Source 中包含一些其他的内容的话,可以自由地使用 metadata ,并在内部包含一系列键值对。由于 createSource 执行完成后会返回一个包含 Source 对象,类似如下:
{
"id": "src_16xhynE8WzK49JbAs9M21jaR",
"object": "source",
"amount": 1099,
"client_secret": "src_client_secret_UfwvW2WHpZ0s3QEn9g5x7waU",
"created": 1445277809,
"currency": "usd",
"flow": "redirect",
"livemode": true,
"owner": {
"address": null,
"email": null,
"name": "null",
"phone": null,
"verified_address": null,
"verified_email": null,
"verified_name": "null",
"verified_phone": null
},
"redirect": {
"return_url": "https://shop.example.com/crtA6B28E1",
"status": "pending",
"url": "https://hooks.stripe.com/redir ... ot%3B
},
"statement_descriptor": null,
"status": "pending",
"type": "alipay",
"usage": "single_use",
"alipay": {
"statement_descriptor": null,
"native_url": null
}
}

其中的 redirect 只要访问了就会自动被 Stripe 跳转到支付宝家的支付页面上,所以我们最后会有一行:
window.location.replace(result.source.redirect.url);
将用户跳转过去,然后用户扫码付钱:


用户这边的事情就结束了。
服务器逻辑
用户的事情结束了,服务器端就需要开始处理用户的请求了,一个简单的方法如下,在用户付款完成后 Stripe 会跳转回我们 JS 中定义的 return_url 并附带一些参数,类似如下:
https://xxx.xxx.xx/buy?client_secret=src_client_secret_xxxxxxxxx&source=src_xxxxxxxxx


这个时候我们可以通过服务端来解析 src_xxxxxxxxx 得知是谁在付钱,并完成后续的操作:
\Stripe\Stripe::setApiKey('sk_xxxxxxxxxxxxxx');

// 获取 URL 中 source 字段
$source_id = filter_input(INPUT_GET, 'source', FILTER_SANITIZE_URL);
$source_object = \Stripe\Source::retrieve($source_id);

// 先确认一下用户付了钱,别有 Object 就直接开始整...
$status = $source_object->redirect->status;
if($status == "failed")
{
// 如果用户没有付钱,我们该怎么做?
}
else {
// 从临时钱包从把钱扣了~
\Stripe\Charge::create([
'amount' => 1988,
'currency' => 'hkd',
'source' => $source_id,
]);
// 有了 Object 之后我们可以提取出对应的用户邮件地址或者别的信息,比如邮件地址可以这样提取
$user_email = $source_object->owner->email;
// 然后这里自己根据 email 找到对应用户完成接下来的步骤,比如把文件通过邮件发给用户,给用户头像加个 Buff 啥的~
}

顺便可以登录 Stripe 后台看看~


微信实现

其实也非常的简单,只需要将上一步的type改为wechat,同时返回source中的source.wechat.qr_code_url转为二维码就好了
 

var wechatCallback = function (source) {
generateQRCode(source.wechat.qr_code_url);
}
function generateQRCode(value) {
var qrEle = document.getElementById("qrcode");
var qrcode = new QRCode(qrEle, {
width: 100,
height: 100
});
qrcode.makeCode(value);
qrEle.style.display = 'inline-block';
}
二维码出来后, 扫码就会得到如下结果
 

 
不过这种方法只是说可以用而已,最好的方法可以参考 Best Practices for Using Sources 来接受 Webhook 多次验证,但这个就不在本文的范围内了。

由于是第一次接触支付领域,上述步骤中可能还是会有不少坑或者啥的(所以别直接在生产环境照抄,写完之后一定要多 Review 几遍逻辑漏洞),不过这个至少是一个可用最小模型了,还有不少可以改进的地方,比如浏览器端的函数其实可以异步拉起,这样可以在网页上弄一个 Modal 弹窗,看上去更加用户友好一些。
 
 
补充说明(04/06/2020):
 
关于注册 Stripe 账号

和注册支付宝账号一个道理,首先注册账号,然后绑定自己银行卡,BUT, 就像前面提到的,不支持中国,所以就算注册成功,也没法激活,也就没法收款。

 
 
 
对于中国商家怎么办呢,我能想到的就只有这3个办法:

自己去支持国家去办理张银行卡
使用国外的朋友银行卡
使用Atlas

对于James来说,因为他是新西兰kiwi,所以他可以创建他的主账号,然后添加我的stripe账号到他team memeber账号列表中,这样我就可以访问他账户下所有开发者需要的权限。邀请成功后,Dashboard页面

 

 
Stripe.js & Elements

当然对于如果你觉得Checkout的方式集成度太高,不够灵活,那Stripe.js是你最好的选择。

Stripe.js其实就是客户端的一个JS核心类库,Elements是它的UI类库,其实上面的Checkout代码就是Stripe使用两者给我们封装好了的,避免我们直接接触敏感信息,但是其实质都是一样的,都是用来创建source。这里就直接贴出客户端的代码了(这里没有用到Elements做UI,因为就是一个按钮支付,太简单,所以没用到):
var stripe = Stripe('pk_live_xxxx');

function alipay(amount) {
showLoading();
stripe.createSource({
type: 'alipay',
amount: parseInt(amount),
currency: 'gbp', // usd, eur,
redirect: {
return_url: 'https://xxx.eu/pay/result.html'
},
}).then(function (response) {
hideLoading();
if (response.error) {
alert(response.error.message);
}
else {
processStripeResponse(response.source);
}
});
}

function processStripeResponse(source) {
window.location.href = source.redirect.url;
}





 
0
Pay Tips 05-04-20

0 comments

If you wanna get more accurate answers, Please Login or Register