这段时间在做支付相关的工作,由于业务主要是面向国外的用户,因而就接触了部分国外的支付支付相关的平台。接下来的内容主要是初步看了 Stripe 平台的文档所了解到的基本内容,后面会在使用的过程中不断地进行完善。
Stripe - 基于API的便捷支付渠道 中对Stripe所提供的功能/产品给了较为不错的参考。
在我写这篇博客之时(2019-04-07),stripe已经支持32个国家的使用,而我在查找资料的时候,看到的基本都是说25个国家。支持的国家(https://stripe.com/global)
从列表中我们可以看到,其实中国大陆是还没有被支持的。为了能够进行收钱,我们需要一个在Stripe支持国家列表中的国家的银行账号。如果中国的企业单位希望使用的话,可以通过 Stripe 的 Atlas 创建一个美国的银行账号。更多关于 Atlas 可以看下 Atlas 的主页介绍,或者看下36氪的《为什么说想把业务做到海外去的公司,都需要关注 Stripe Atlas ?》。这里就不做展开介绍了。
有朋友使用过PayPal和Square。PayPal的开发难度更大,隐藏的价格远比想象中的还要高,表面上看,每笔钱进来,需要付给PayPal 2.99%的费用,但实际上确付了5%还要高。而且在使用新的PayPal账号的时候,发现新账号不接受信用卡支付。而Square相对于PayPal,开发难度要更简单。但在开发App端的支付时,发现Square的SDK仅仅支持较低版本的安卓系统,这就不得不放弃Square了。
不同的平台有不同的优缺点,总的来说。Stripe开发简单,支付的支付方式很多(支付宝和微信支付目前都已经支持了),适合创业团队使用。而Square对线下的支付有着很好的支持,如果有线下支付的需要的业务可以考虑使用Square。而PayPal更加适合大型企业的使用。
支付平台的选择不是这篇文章的重点,如果想要了解更多的对比细节,可以查看如下的参考文章。
点击这里,可以查看Stripe平台的价格。
参考
支付流程。整个支付流程还是比较简单的,总的来说就是前端通过Stripe平台获取支付令牌,后端利用这个支付令牌进行请求Stripe进行实际支付。用户的信用卡敏感信息不会通过我们的后台,Stripe会帮我们处理复杂的PCI compliance。这里会涉及到企业的免责问题。
为了方便我们进行各种测试,Stripe 为我们的测试提供了足够的测试账号。
后台得到支付使用的token之后,在进行收款时, 设置capture为true,Stripe则会直接进行收账。
// Set your secret key: remember to change this to your live secret key in production
// See your keys here: https://dashboard.stripe.com/account/apikeys
Stripe.apiKey = "sk_test_yoursecretkey";
// Token is created using Checkout or Elements!
// Get the payment token ID submitted by the form:
String token = request.getParameter("stripeToken");
Map<String, Object> params = new HashMap<>();
params.put("amount", 999);
params.put("currency", "usd");
params.put("description", "Example charge");
params.put("source", token);
// params.put("capture", true);
Charge charge = Charge.create(params);
每个token只能使用一次,而且会很快就会失效了。事实上,token本身是不会失效的,但是CVC信息在很短的时间内可用,在延迟之后使用令牌会导致在没有CVC信息的情况下执行收费。
Stripe支持分步骤支付,可以先授权支付,然后等待稍后进行结算。交易的资金将由发卡单位进行担保。授权的有效期为七天,如果没有及时进行收费,授权将会被取消并释放资金。利用两步支付,我们可以在支付流程中增加一个支付审核的流程,用于防止支付欺诈。
第一步: 创建一个uncaptured的charge
// Set your secret key: remember to change this to your live secret key in production
// See your keys here: https://dashboard.stripe.com/account/apikeys
Stripe.apiKey = "sk_test_yoursecretkey";
// Token is created using Checkout or Elements!
// Get the payment token ID submitted by the form:
String token = request.getParameter("stripeToken");
Map<String, Object> params = new HashMap<>();
params.put("amount", 999);
params.put("currency", "usd");
params.put("description", "Example charge");
params.put("source", token);
params.put("capture", false);
Charge charge = Charge.create(params);
第二步: 进行capture
// Set your secret key: remember to change this to your live secret key in production
// See your keys here: https://dashboard.stripe.com/account/apikeys
Stripe.apiKey = "sk_test_yoursecretkey";
Charge charge = Charge.retrieve("ch_mh4nUTTkwbhC3i9gpcm0");
charge.capture();
Stripe 可以设置每笔账单的邮件接受,每个charge会对该邮箱发送email。
params.put("receipt_email", "[email protected]");
Stripe支持添加Metadata到自己的支付请求中,将会有助于自己管理每笔支付信息。这些信息只有自己可以看到,而客户是看不到的。如果启用的Radar,可以在Radar利用这些信息的设置Radar规则。
Map<String, String> metadata = new HashMap<>();
metadata.put("order_id", 6735);
params.put("metadata", metadata);
支付过程中,会存在支付失败的结果。交易结果中会含有失败的类型和原因描述。建议对每种失败类型进行处理,错误列表看: https://stripe.com/docs/declines/codes。decline-codes列表对应outcome.reason
。考虑是否需要将这些错误信息转化为平台的错误信息。需要注意的是,前端在获取授权的时候会有哪些错误返回,又应该如何进行展示。并不是所有的交易失败都可以确定具体的原因,这些失败通常会返回 code
为 generic
,这时候最好让你的客户直接联系发卡银行,咨询具体原因。
...
outcome:
{
network_status: "declined_by_network"
reason: "expired_card"
risk_level: "normal"
seller_message: "The bank returned the decline code `expired_card`."
type: "issuer_declined"
},
...
造成失败的原因可以总结如下:
type:"issuer_declined"
)
type: "invalid"
。这个在生产环境中应该尽量避免, 该类型失败的支付不会显示在Dashboard中)如果Stripe屏蔽了一些你知道是合法安全的支付,你可以在Dashboard中标记该卡为安全。标记为安全不会继续尝试本次支付,但是会允许其之后的支付。
如果希望在集成环境中对支付结果进行自动的处理,可以通过如下两种方式对charge的outcome进行处理:
1. 处理支付失败时返回的API错误。 对于被冻结的和信用卡发行方拒绝的付款,错误包括该charge的ID,然后可以使用该charge的ID来retrive该charge。
try {
// Use Stripe's library to make requests...
} catch (CardException e) {
// Since it's a decline, CardException will be caught
System.out.println("Status is: " + e.getCode());
System.out.println("Message is: " + e.getMessage());
} catch (RateLimitException e) {
// Too many requests made to the API too quickly
} catch (InvalidRequestException e) {
// Invalid parameters were supplied to Stripe's API
} catch (AuthenticationException e) {
// Authentication with Stripe's API failed
// (maybe you changed API keys recently)
} catch (APIConnectionException e) {
// Network communication with Stripe failed
} catch (StripeException e) {
// Display a very generic error to the user, and maybe send
// yourself an email
} catch (Exception e) {
// Something else happened, completely unrelated to Stripe
}
2. 使用webhooks监听事件提示。 当支付失败时,charge.failed
事件就会被触发,返回的结果会包含Charge对象。
支付平台都应该注意到交易过程中如何处理交易纠纷和防范交易欺诈,以免在交易过程中处于不利地位,设置是亏钱亏货。这部分内容会很多,你可以点击这里到平台中查看更多的信息。下面的内容为我在整理的大概内容。
当持卡人向发卡单位提出付款问题时,就会发生争议(也称为退款)。发行方产生正式争议,立即撤销支付。支付的金额,连同卡网收取的另一笔85元争议费(适用于香港的用户),会从你的帐户结余中扣除。
我们需要做的是,如何证明之前的支付是有效的。应该通过 Stripe的Dashboard进行提交资料,因为上面会有一些指导。我们应该在纠纷有效时间内提供证据,否则将无法提供证据. 具体时间有纠纷中提供的信息。而且我们只能提交一次资料,如果资料没有提交完整,在尝试通过email进行提交等都是没有用的。
处理纠纷是要给Stripe交手续费的。
对方获胜: 纠纷金额退换给对方和卡网收取纠纷费用HK$85.00。
我方获胜: 卡网收取纠纷费用HK$85.00,纠纷金额退换给我们。
纠纷创建和关闭时,Stripe都会自动发送一封邮件给你,以及触发 webhook的 charge.dispute.created
和 charge.dispute.closed
事件。
纠纷中需要提交的证据(后台开发中需要注意保留这些信息)
为了防范纠纷和欺诈,我们需要在开发的时候留意一些必要的操作。
1. 收集尽可能多的支付信息
有些信息时在前端获取token的时候收集,有些是在后台进行charge的时候收集。(这里需要确认那些信息需要提供给Stripe,进行Radar,以及提供这些信息给Stripe是否安全)。
2. 使用校验检查
当进行支付的时候,Stripe会将我们提供的信息提交到发卡单位,发卡单位会对这些信息进行校验。但验证不通过时,这次支付就又可能就是一次欺诈。因此,强烈推荐收集: CVC,Postal code 和 billing address。这些都有助与帮助检测潜在的欺诈行为。
每个Charge Object中都会含有校验信息. 具体见verification response。当然也可以在Dashboard中看到校验结果。
Card verification code check (CVC) Businesses are not permitted to store the CVC。所以一般而言,CVC只会通过物理丢失,或者在不安全的网站输入而被盗。
Address verification (AVS) AVS checks determine whether these pieces of information match the billing address on file with the card issuer。Radar 默认阻止不通过 postal code verification的支付。注意,如果使用了billing address verification,当用户填写的地址和用户申请信用卡时候的地址不一致,将无法完成支。
如果你有多个商业系统,建议使用不同的账号。Stripe提供了Multiple Account的功能来设置不同的账号。
在平时我们使用绑定信用卡到支付平台的时候,会先进行少量金额的支付,比如一美元,再进行自动退款,应该是为了通知信用卡持有者有人正在使用他的卡进行支付。
线上欺诈的类型。线上欺诈和对实体商业的欺诈有着根本上的不同,因为在线上你很难确定你实际销售的对象是谁。在接受线上交易是,你非常有必要知道有哪些欺诈方式。
识别潜在的欺诈。Stripe为我们给出了一些建议,让我们能够尽量提前识别出潜在的欺诈。当然,Stripe的Rada功能也会自动识别出具有高欺诈风险的信用卡付款。如下为内容概要,更加详细的内容以官网内容为准。
对于反常的订单(订单金额特别大, 量特别大), 则需要特殊处理, 比如通过电话确认或者email确认之类的.
可以在交易流程中增加一个支付审核的流程,并使用两步支付的方法来进行实现。
data-zip-code="true"
to the above and make use of Radar’s built-in rules to decline payments that fail verification.
退款一旦发生, 将不会被停止。
全部退款
Map<String, Object> params = new HashMap<>();
params.put("charge", stripeToken);
Refund refund = Refund.create(params);
部分退款
Map<String, Object> params = new HashMap<>();
params.put("charge", stripeToken);
params.put("amount", 1000);
Refund refund = Refund.create(params);
可以使用charge.refund.updated
监控退款事件。如果charge是通过Customer的方式发生的,且在Customer中含有email信息,则在退款发生时,会发送消息给用户。
支付精度。在实现支付的时候,我们需要关注Stripe平台的精度。
支付单位
默认使用最小单位, 如美元用美分.对于零位小数货币,仍然以整数形式提供金额,但不乘以100。例如,要收取500英镑,只需提供500英镑的金额。(零位小数货币列表supported charge currencies)
最小支付金额
由于Stripe的处理费结合了一小部分固定金额和百分比,因此在使用Stripe创建费用时强制执行最低金额。这可以确保您不会因收费而损失金钱。您可以收取的最低金额取决于支付的银行帐户结算货币。
最大支付金额
The only limit to the maximum amount you can charge a customer is a technical one. The amount value supports up to eight digits (e.g., a value of 99999999 for a USD charge of $999,999.99).
需要设置美元的最大支付值为 $999,999.99 . 或者设置一个其他的值.
防止Stripe IP无法访问。
因为调用接口需要访问到Stripe的服务器,以及在利用webhooks接受支付回调时,接受Stripe服务器的访问。因此需要在系统里面确保Stripe服务器的可访问性。
为了能够及时的知道Stripe Ip的变动,可以订阅Stripe的API announce mailing list
使用webhooks可以监控支付的一些触发事件。
这里的介绍中还没有涉及到其他的支付方式,事实上Stripe支持非常多的支付方式,都已经支持支付宝和微信,设置还支持比特币支付。Stripe有一个Payment Intents API可以对多种支付方式做更好的实现。
善用Card和Customer功能,可以方便对支付的管理和提供用户的体验。这里的内容都比较多,暂时就不展开详细介绍了。
Stripe提供账单的功能对指定用户进行周期性的收费,这就可以用与实现类似会员自动续费的功能了。
除非你有PCI的认证,否则请不要在数据库中保存用户的信用卡信息,甚至让这些信息经过你的服务器。
根据苹果公司的规定,出售数字化内容需要使用应用内购买,比如电子游戏附带的游戏级别和应用给予用户的虚拟物品。而对于像衣服这样的实物,则允许使用像Stripe 这样的其他支付方案。使用Stripe支付,就可以不用给苹果交高额的费用。