支付宝扫码支付和微信扫码支付共存的情况下,如何避免同一个订单被多次支付

       前面分享了一篇 JAVA后端调用微信支付“统一下单”接口实现微信二维码扫码支付 的博客,虽然二维码在生成的时候可以设置有效期,但是这依旧不能保证整个业务流程和数据的安全。因为通常网站提供给用户的支付方式不止一种,除了微信支付,可能还有支付宝和银联支付等。

       我们不妨先来假设下面几个场景:

       场景一:张三在你的网购平台下了一个订单,起初是选择微信二维码扫码支付的,但是打开微信时发信微信零钱余额不足,于是张三就把这个二维码截图发给自己的基友李四,等了五分钟,李四可能因为手头有事要忙迟迟没有支付,这时张三实在没耐心了,就想起自己的支付包刚好还有点钱,于是就用支付宝支付成功了。恰巧此时李四那边也忙完自己的事情了,拿着自己的微信也成功扫码支付了。这是就造成一个订单,被成功支付两次了。

       场景二:张三在你的网购平台下了一个订单,选择微信二维码扫码支付,因自己余额不足,将生成的二维码发给李四,由李四代付,大约过了五分钟,李四那边有事忙着一直没有支付,此时张三突然不想买了,于是取消了订单,过了几分钟才想起来要通知李四取消订单,恰好此时李四那边已经成功扫码支付了。这就造成已取消订单被支付成功的情况。


        针对这种一个订单被多次支付和已取消订单被支付的情况,平台需要提供退款的功能,并且添加流水的操作应该由定时任务来执行,具体的流程准备另写一篇博客说明(如果我还记着这回事的话)。但是这就足够了吗?所谓种什么因得什么果,为什么不从源头上降低上面这两种情况发生的概率呢!假设订单取消后的30秒内,生成的微信二维码依旧有效,如果这还勉强能够说得过去的话,那么用户订单取消5分钟后(甚至更长时间),这个二维码还能被正常使用,那这就说不太过去了吧。你可能会说,可以在生成二维码的时候设置二维码的有效时间是5分钟或者更短(这个可参考 微信支付接口调用之二维码失效时间的设置),但是你总会遇到一些需求,比如要求二维码的有效时间跟着平台订单的有效时间走的情况,这时二维码的有效时间就不只10分钟了吧。

      针对这种情况,我的解决方案是:新建一张表 t_qr_code,用来存储生成的二维码图片路径,并且保存二维码的失效时间。表的结构如下:

CREATE TABLE `t_qr_code` (
  `pk_id` varchar(50) NOT NULL COMMENT '主键',
  `order_id` varchar(50) DEFAULT NULL COMMENT '商户订单号',
  `code_url` varchar(255) DEFAULT NULL COMMENT '二维码保存的路径',
  `status` tinyint(3) DEFAULT '1' COMMENT '二维码状态,1,正常;2,已调用微信api使之失效;3,需要调用微信api关闭订单;',
  `expire` datetime DEFAULT NULL COMMENT '二维码失效日期',
  `create_date` datetime DEFAULT NULL COMMENT '二维码生成时间',
  `update_date` datetime DEFAULT NULL COMMENT '本记录更新时间',
  PRIMARY KEY (`pk_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

 

该表有以下几个用处:
(1)当用户关闭二维码所在页面,下次再次对这个订单发起微信二维码扫码支付时,如果这时二维码有效期超过15秒,就依旧返回这个二维码图片和失效时间,而不会重新跟微信请求。当然这个也需要前端根据这个二维码的失效时间做一些特效处理以提高用户体验,比如二维码失效了,就弹出一个按钮让用户点击刷新。
(2)当用户取消订单的时候,查询 t_qr_code 表中是否有未是失效的二维码,如果有,修改status=3,由定时任务调用微信关闭订单接口的接口。
(3)如果用户一开始选择二维码扫码支付,成功生成了二维码,但是最终却是选择其他方式支付成功的,其它方式的支付接口中,需要执行下面这台sql语句:

update t_qr_code set status=3 where expire>now() and status=1 where order_id=#{orderId}

      大致思路大概就如此,顺便提醒一下,你的调用微信“关闭订单”接口的定时任务的扫描间隔时间应该设置的尽量短一些,这个时间越短,造成失效订单被支付的概率就越低。在分享源码之前,请参考官方文档  ,值得注意的是,微信H5支付,微信JSAPI支付,微信Native支付(即二维码扫码支付)和微信APP支付 的关闭订单接口都是同一个。感兴趣的可以去  微信支付  官网逐一查看。此接口本人亲测有效,测试方法:当微信生成二维码的时候,设置的二维码有效期是30分钟,但是在生成二维码3-5分钟后,调用下面这个关闭订单的接口,等再次拿微信扫描二维码的时候,微信提示该订单已失效。官方文档提示要再二维码生成的5分钟后再调用关闭订单的接口,但是实际测试发现3分钟后也能成功取消。

       下面分享具体的实现代码,我把他封装到了一个工具类中,返回的参数都在map中,如果想知道有哪些参数,可以在自己的控制台打印一下或者参考官方文档 ,下面分享代码:

package wxpay;


import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;


public class WxApiUtils {

    /**
     * 关闭微信订单接口
     * @param orderId你自己网站生成的订单号,注意要和你申请二维码时候用的订单号一致,
            看过我前面那边申请二维码支付的朋友会发现,此时我的这个orderId应该是要带有
            支付标识的,如果我网站的订单号是:123456 ,那么此时的这个参数
             orderId=123456_NAVTIVE ,因为我申请二维码用的就是这个带标识的订单后
     * @return
     */
    public static Map<String, String> closeOrder(String orderId){

        String appid =Constant.APP_ID;
        String mch_id = Constant.MCH_ID;
        String out_trade_no = orderId;
        String nonce_str = WXUtil.createNoncestr().substring(1, 32);

        SortedMap<String, String> packageParams = new TreeMap<String, String>();
        packageParams.put("appid", appid);
        packageParams.put("mch_id", mch_id);
        packageParams.put("out_trade_no", out_trade_no);
        packageParams.put("nonce_str", nonce_str);

        String sign = WXUtil.createSign(packageParams);

        packageParams.put("sign", sign);

        String xml =WXUtil.ArrayToXml(packageParams);
        logger.info("map转换成xml的结果:"+xml);

        String createOrderURL = "https://api.mch.weixin.qq.com/pay/closeorder";

        return GetWxOrderno.getReturnUrl(createOrderURL, xml);

    }


}

 

涉及到的所有工具类和实体类,以及一些依赖的jar包,在我的另一篇博客 JAVA后端调用微信支付“统一下单”接口实现微信二维码扫码支付 中都能找到,时间有限,此处不再上传。

至此,分享结束,希望对各位有帮助。

阅读终点,创作起航,您可以撰写心得或摘录文章要点写篇博文。去创作
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
快捷支付WEB接入说明: 1、目录说明 |-- C#开发包 |-- Java开发包 |-- PHP开发包 |-- openssl-0.9.8k_WIN32(RSA密钥生成工具).zip(商户使用RSA 签名时用到) |-- ReadMe |-- RSA密钥生成工具使用说明.doc(商户使用RSA 签名时用到) |-- 对于商户测试需规则免疫的申请 |-- 连连银通商户域名IP应用标识配置申请表V2.1.1 2、接入说明 快捷支付WEB版,支付请求跳转连连收银台,用户通过输入预留手机短信验证码进行支付。 请参考Demo, Demo中支持两种支付接入方式(标准版,卡前置) 注意:Demo中的所有数据均为测试数据,请接入时参考接入文档《连连支付WEB支付商户接口说明书》字段说明组装正式数据。 2.0、适用场景 2.0.1、标准版:适合商户不对用户支付环节进行处理的情况,即商户产生订单后,直接使用WEB进行支付,用户的银行卡管理完全有连连收银台页面负责。 2.0.2、卡前置:多适用于理财类商户,商户界面需要对支付时的银行卡进行管理,包括首次输入卡号,历次产生绑定卡列表。 注: 接口为同一个,卡前置模式请求参数多传 card_no 银行卡字段。 2.1、重要参数介绍 注: 参数说明以 【连连支付WEB支付商户接口说明书】 为准 文档地址:http://open.lianlianpay.com/#cat=35 no_agree 签约协议号。用户银行卡首次签约支付成功,生成的一个协议编号,可用于二次支付,解绑卡。 商户号下 每个用户号 每张银行卡对应协议号唯一,解绑后才会改变。 risk_item 风险控制参数。快捷支付时必传,此参数用于风险控制,保障用户资金安全的途径之一。 2.2、接入重要说明 快捷支付web接口对接开发完后,需要在我们内部走上线流程,技术需要确认 风控参数 和 异步通知。 风险控制参数,快捷支付必传,商户上线时,风控部门会审核该参数;误传或漏传可能导致交易误拦截,影响正常交易。 异步通知,订单支付成功会有异步回调商户服务器,修改订单状态以异步通知为准,商户需要做好重复通知的控制,避免重复通知导致重复入账。 2.3、API接口说明 2.2.1、商户支付结果查询接口 //可查订单状态、支付失败原因 2.2.2、银行卡卡bin查询接口 //可查银行卡有效性、所属银行、银行卡类型、银行卡支持限额 2.2.3、用户签约信息查询API接口 //可根据用户号查询用户签约绑定的银行卡和对应协议号 2.2.1、银行卡解约接口 //可根据协议号进行解绑卡 3、常见问题 3.1、1003 风控拦截 风控拦截是我们公司风控部门控制,认为有风控的订单会进行风控拦截,一般测试频繁时遇到比较多。可以申请测试白名单。 申请表在目录中 《对于商户测试需规则免疫的申请》 //测试白名单申请表 3.2、1001 商户验证签名失败 验签失败是请求我们web接口时,签名异常。 3.2.1、待签名串格式不正确,我们这把请求数据中的所有元素(除sign本身)按照“key值=value值”的格式拼接起来,若顺序或者格式不正确的话,就会包错。 注:请求时字段前后做去空格处理,请求参数要和签名的参数一致,空字段不参与签名。 3.2.2、秘钥配置错误,商户正式商户号的key 值可在商户站(https://yintong.com.cn/merchant/trader/login.htm) -》安全中心-》商户秘钥维护 进行修改。 3.3、1006 来源域名非法,1008 来源IP有误 web端支付需要报备来源域名,API接口需要报备服务器IP。 注: 申请表在目录中 《连连银通商户域名IP应用标识配置申请表V2.1.1》 //域名IP应用标识申请表 4、demo 接入指南 4.1、php log.txt 可查看商户情况的日志,会打印商户请求的待签名串。 llpay.config.php 是配置文件,可对商户号和秘钥等参数进行修改,可修改商户号,签名方式和MD5_KEY 值。若修改为RSA 签名可在 key 文件夹下修改rsa_private_key.pem 秘钥 notify_url.php 是异步通知文件 用户处理接受通知异步通知内容。 return_url.php 是同步通知文件 ,用来处理同步处理内容。 index.php 连连支付接口入口文件 4.2、java \src\com\llpay\client\config\PartnerConfig.java 可配置商户号 签名方式和秘钥 \src\com\llpay\client\config\PartnerConfig.java 配置支付地址 \src\com\llpay\client\pay\ReceiveNotifyServlet.java 异步通知类 \src\com\llpay\client\pay\ToPayServlet.java 支付处理类 \src\com\llpay\client\pay\InfoQueryServlet.java 卡bin 查询 和签约查询类 /authpaywebdemo/toPay.htm 连连支付入口 /authpaywebdemo/notify.htm 异步通知说明 4.3、c# PartnerConfig.cs 可配置商户号 签名方式和秘钥 Default.aspx plainPay.aspx 第一个页面 plainPay.aspx.cs plainPay.aspx 普通接入 prepositPay.aspx , prepositPay.cs卡前置接入 notify_url.aspx, notify_url.aspx.cs 异步通知页面 urlReturn.aspx,urlReturn.aspx.cs 同步通知返回页面
### 回答1: 我可以为您提供一些关于使用Node.js来实现微信扫码支付的指南:首先,您需要在Node.js中安装微信SDK,这可以通过npm完成,然后您需要创建一个微信支付订单,并使用微信SDK调用微信支付API来实现扫码支付。在完成扫码支付后,您可以获取订单的状态信息,并进行相应的处理。 ### 回答2: 使用Node.js编写微信扫码支付需要以下步骤: 1. 首先,确保已安装Node.js环境和相关的npm包管理器。 2. 创建一个新的Node.js项目并初始化,在项目目录中运行`npm init`命令来生成`package.json`文件。 3. 在项目中使用npm下载微信支付的官方SDK,可以通过运行`npm install @vivaxy/wechat-pay`来安装。 4. 引入支付SDK并初始化微信支付: ```javascript const wechatPay = require('@vivaxy/wechat-pay'); // 初始化微信支付 const payment = new wechatPay.Payment({ appid: 'YOUR_APPID', mch_id: 'YOUR_MCHID', key: 'YOUR_KEY', // ... }); ``` 5. 创建Express应用并定义扫码支付的路由: ```javascript const express = require('express'); const app = express(); app.get('/pay', async (req, res) => { // 生成订单号和价格 const outTradeNo = 'ORDER_NUMBER'; const totalFee = 100; // 单位为分 // 构建扫码支付参数 const result = await payment.getNativePayParams({ outTradeNo, totalFee, body: 'ORDER_DESCRIPTION', // ... }); // 返回参数给前端 res.send(result); }); app.listen(3000, () => { console.log('Server running on port 3000'); }); ``` 6. 启动应用,并访问`http://localhost:3000/pay`获取扫码支付参数。 7. 在前端页面中使用生成的扫码支付参数调用微信支付接口,生成支付二维码供用户扫码支付。 8. 在后端根据微信异步通知或者查询支付结果的方式完成支付的验证。 以上是使用Node.js编写微信扫码支付的基本步骤,具体的实现和细节可能根据项目需求和微信支付官方文档的变动而有所不同。 ### 回答3: 使用Node.js编写微信扫码支付的流程如下: 1. 首先,在微信商户平台上注册并获取到商户号和密钥。商户号用于标识商户身份,密钥用于签名和加密通信数据。 2. 安装Node.js并使用npm命令行工具安装相关依赖包,包括express、request、crypto和xml2js等。 3. 创建一个Express应用程序,并设置路由来处理微信支付的相关请求。 4. 在服务器端生成一个唯一的订单号,将订单号和一些必要的信息(如订单金额、商品描述等)传递给微信支付接口。 5. 根据微信支付文档的要求,对订单信息进行签名。可以使用crypto模块中的HMAC-SHA256算法来创建签名。 6. 将订单信息和签名组装成XML格式的数据,并通过request库发送POST请求到微信支付统一下单接口。 7. 微信支付接口会返回一个预支付交易会话标识prepay_id。页面通过此标识生成一个二维码,供用户扫码支付。 8. 使用轮询的方式查询用户是否已完成支付。可以使用setInterval函数定时向微信查询支付状态。 9. 当用户支付成功后,微信服务器会发送一个支付成功的通知(异步通知)到服务器的回调地址。应在回调函数中验证通知的真实性。 10. 如果验证成功,修改订单状态,并返回给微信服务器一个成功的响应。微信服务器会将支付结果通知给用户的手机。 11. 可以创建一个页面用于显示支付结果,包括支付成功、支付失败等页面。 以上是使用Node.js编写微信扫码支付的大致流程。在具体实现过程中,还需要考虑到安全性、异常处理、用户体验等方面的问题。希望对你有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

风难追

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值