后端微信刷卡支付功能实现

2023-10-29 12:59

本文主要是介绍后端微信刷卡支付功能实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 微信刷卡支付实现(普通商户)
    • 一、微信公众号配置
    • 二、功能的具体实现

微信刷卡支付实现(普通商户)


文章包含查询订单、撤销订单、申请退款、查询退款
官方参考文档

一、微信公众号配置

  1. 微信公众号申请
    地址
  • 申请流程
  • 记下公众号开发者ID(appid)
  1. 申请商户
  • 在微信公众平台完成商户申请。
    在这里插入图片描述
  1. 设置API安全
    1、下载API证书。
    2、设置并记下API秘钥(很重要)。
    在这里插入图片描述

二、功能的具体实现

  1. 下载官方Demo
  2. 创建MyConfig类并继承WXPayConfig抽象类
  • 查看demo中的README.md文件。
  • 将appid、mchid、key以及API证书路径添加到方法中。

具体代码:

public class MyConfig extends WXPayConfig {private byte[] certData;public MyConfig() throws Exception {String certPath = "C:/Users/syf/Desktop/cert/apiclient_cert.p12";//证书存放地址File file = new File(certPath);InputStream certStream = new FileInputStream(file);this.certData = new byte[(int) file.length()];certStream.read(this.certData);certStream.close();}@OverridePublic String getAppID() {return "w";  //添加appid}@OverridePublic String getMchID() {return "; //添加mchid}@OverridePublic String getKey() {return "ig5jed04hrmo"; //添加key}@OverrideInputStream getCertStream() {ByteArrayInputStream certBis = new ByteArrayInputStream(this.certData);return certBis;}@OverrideIWXPayDomain getWXPayDomain() {IWXPayDomain iwxPayDomain = new IWXPayDomain() {public DomainInfo getDomain(WXPayConfig config) {return new IWXPayDomain.DomainInfo(WXPayConstants.DOMAIN_API, true);}public void report(String domain, long elapsedTimeMillis, Exception ex) {}};return iwxPayDomain;}
}
  • 将整个demo中复制到项目中。
  1. 刷卡支付
    应用场景:
    收银员使用扫码设备读取微信用户刷卡授权码以后,二维码或条码信息传送至商户收银台,由商户收银台或者商户后台调用该接口发起支付。
    1.1、创建controller类和刷卡支付方法
    1.2、创建MyConfig对象和WXPay对象方法。正式环境时:new WXPay(config),若使用沙箱环境:WXPay wxpay = new WXPay(config, true, true)。
    1.3、 创建map对象,将必填字段(除appid,mch_id,nonce_str,sign,sign_type。microPay方法中已经字段将这些字段添加到参数中)添加到map中(注意查看参数要求!)。
    1.4、调用microPay方法并传入map参数。
    1.5、microPay方法返回Map类型参数。仔细阅读返回参数字段以及条件。根据返回结果决定下一步操作。(注意return_code和result_code返回不同状态时返回的参数)
    提醒1:提交支付请求后微信会同步返回支付结果。当返回结果为“系统错误”时,商户系统等待5秒后调用【查询订单API】,查询支付实际交易结果;当返回结果为“USERPAYING”时,商户系统可设置间隔时间(建议10秒)重新查询支付结果,直到支付成功或超时(建议30秒);
    提醒2:在调用查询接口返回后,如果交易状况不明晰,请调用【撤销订单API】,此时如果交易失败则关闭订单,该单不能再支付成功;如果交易成功,则将扣款退回到用户账户。当撤销无返回或错误时,请再次调用。注意:请勿扣款后立即调用【撤销订单API】,建议至少15秒后再调用。撤销订单API需要双向证书。
    具体代码实现:
public MyJsonResult microPay(HttpServletRequest request,HttpServletResponse response) throws Exception{MyConfig config = new MyConfig();WXPay wxpay = new WXPay(config);//上线环境用         //WXPay wxpay = new WXPay(config, true, true);//使用沙箱环境    String total_fee=request.getParameter("total_fee");String auth_code=request.getParameter("auth_code");    //请求刷卡支付用参数mapMap<String, String> data = new HashMap<String, String>();//接收返回参数Map<String, String> resp = new HashMap<String, String>(); data.put("auth_code", auth_code);data.put("device_info", "qiang1");data.put("body", "停车收费!");data.put("out_trade_no", WXPayUtil.generateNonceStr());data.put("total_fee", total_fee); data.put("spbill_create_ip","218.28.14.143"); try {resp = wxpay.microPay(data);        } catch (Exception e) {e.printStackTrace();}//请求成功 if("SUCCESS".equals(resp.get("return_code"))){//不需要输如密码时if("SUCCESS".equals(resp.get("result_code"))){//验证签名if(wxpay.isPayResultNotifySignatureValid(resp)) {return new MyJsonResult(ConstantCode.SUCCESS,"支付成功! 已验证签名!");}//String transaction_id=resp.get("transaction_id");//return new MyJsonResult(ConstantCode.SUCCESS,"支付成功!"+transaction_id);//验证签名失败,做出相应的处理return new MyJsonResult(ConstantCode.ERROR,"签名验证失败!");}         	//需要用户输入密码if("FAIL".equals(resp.get("result_code"))){if("USERPAYING".equals(resp.get("err_code"))){//查询订单Map<String,String> qRequestMap = new HashMap<String, String>();qRequestMap.put("out_trade_no", data.get("out_trade_no"));//Map<String,String> resultMap=new HashMap<String,String>();//等待5秒,查询订单try {Thread.sleep(5000);} catch (InterruptedException e1) {//捕获异常e1.printStackTrace();}try {resultMap=checkOrder(wxpay,qRequestMap);} catch (Exception e2) {// TODO Auto-generated catch blocke2.printStackTrace();}if("SUCCESS".equals(resultMap.get("trade_state"))){String transaction_id=resultMap.get("transaction_id");return new MyJsonResult(ConstantCode.SUCCESS,"支付成功!");}        		//每个10秒调用订单查询接口,查看支付结果for(int i=0;i<3;i++){try {Thread.sleep(10000);} catch (InterruptedException e1) {e1.printStackTrace();}try {resultMap=checkOrder(wxpay,qRequestMap);} catch (Exception e2) {// TODO Auto-generated catch blocke2.printStackTrace();}if("SUCCESS".equals(resultMap.get("trade_state"))){String transaction_id=resultMap.get("transaction_id");return new MyJsonResult(ConstantCode.SUCCESS,"支付成功!");}System.out.println("查询支付中。。。。");}//撤销订单Map<String,String> reversResp =new HashMap<String,String>();try {reversResp = wxpay.reverse(qRequestMap);} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}if(reversResp!=null){if("SUCCESS".equals(reversResp.get("return_code")) && "SUCCESS".equals(reversResp.get("result_code"))){return new MyJsonResult(ConstantCode.SUCCESS, "订单已撤销,请重新支付");}return new MyJsonResult(ConstantCode.ERROR, reversResp.get("err_code_des"));}}else{return new MyJsonResult(ConstantCode.ERROR, resp.get("err_code_des"));}}	} //请求失败return new MyJsonResult(ConstantCode.ERROR, resp.get("err_code_des")); 
}
  1. 使用仿真测试系统:
    仿真系统的API协议与正式API完全相同(API接口文档)。只需将正式API的调用URL增加一层sandboxnew路径,即可对接到仿真系统。仿真系统与生产环境完全独立,包括存储层。商户在仿真系统所做的所有交易(如下单、支付、查询)均为无资金流的假数据。
    接入仿真系统的交互流程示例:
    (1)、商户发起刷卡支付请求,使用POST方式调用 https://api.mch.weixin.qq.com/sandboxnew/pay/micropay
    (2)、带sandboxnew 的https请求会被nginx路由到仿真系统。仿真系统根据支付金额(total_fee字段)返回预期报文给商户。同时,落地该笔请求数据;
    (3)、商户发起查单,调用 https://api.mch.weixin.qq.com/sandboxnew/pay/orderquery,带上微信订单号(transaction_id)或商户内部单号(out_trade_no);
    (4)、仿真系统收到查单请求后,根据单号及金额返回预期的查单结果给商户;
    (5)、商户下载对账单,调用 https://api.mch.weixin.qq.com/sandboxnew/pay/downloadbill,仿真系统返回固定的账单格式给商户。
    :账单内容不一定与商户在仿真系统产生的交易完全相同。
    :验收仿真测试系统的API验签密钥需从API获取
  • 获取沙箱key查看
  • 获取API验签秘钥方法参考
public String retrieveSandboxSignKey(WXPayConfig config, WXPay wxPay,Map<String,String> params) {try {String strXML = wxPay.requestWithoutCert("/sandboxnew/pay/getsignkey",params, config.getHttpConnectTimeoutMs(), config.getHttpReadTimeoutMs());if (StringUtils.isBlank(strXML)) {return null;}Map<String, String> result = WXPayUtil.xmlToMap(strXML);//log.info("retrieveSandboxSignKey:" + result);System.out.println(result.get("return_code")+"\n"+result.get("return_msg"));if ("SUCCESS".equals(result.get("return_code"))) {return result.get("sandbox_signkey");}if("FAIL".equals(result.get("return_code"))){return result.get("return_code");}return null;} catch (Exception e) {//log.error("获取sandbox_signkey异常", e);return null;}}
  1. 查询订单(不需要证书)
    1、创建map对象,添加商户订单号(out_trade_no)请求参数。(appid,mch_id,nonce_str,sign,sign_type不用添加,orderQuery方法会自动将这些字段添加到参数中)添加到map中(注意查看参数要求!)。
    2、调用查询订单方法orderQuery方法,根据返回参数判断订单支付情况。(注意查看返回参数!)。
    具体代码实现
MyConfig config = new MyConfig();
WXPay wxpay = new WXPay(config);
Map<String,String> qRequestMap = new HashMap<String, String>();qRequestMap.put("out_trade_no", out_trade_no));//接收返回参数Map<String,String> resultMap=new HashMap<String,String>();        		
try {queryResp= wxpay.orderQuery(map);
} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();
}
if("SUCCESS".equals(queryResp.get("return_code")) && "SUCCESS".equals(queryResp.get("result_code"))&& "SUCCESS".equals(queryResp.get("trade_state"))){System.out.println("支付成功!");returnQuery.put("trade_state", queryResp.get("trade_state"));returnQuery.put("transaction_id", queryResp.get("transaction_id"));return returnQuery;
}
  1. 撤销订单(需要证书)
    1、创建map对象,添加商户订单号(out_trade_no)或者微信订单号(transaction_id)请求参数。(appid,mch_id,nonce_str,sign,sign_type不用添加,reverse方法会自动将这些字段添加到参数中)添加到map中(注意查看参数要求!)。
    2、调用查询订单方法reverse方法,根据返回参数判断订单撤销状态。(注意查看返回参数!)。
    具体代码实现
MyConfig config = new MyConfig();
WXPay wxpay = new WXPay(config);
Map<String,String> reversResp =new HashMap<String,String>();
try {reversResp = wxpay.reverse(qRequestMap);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(reversResp!=null){if("SUCCESS".equals(reversResp.get("return_code")) && "SUCCESS".equals(reversResp.get("result_code"))){return new MyJsonResult(ConstantCode.SUCCESS, "订单已撤销,请重新支付");}return new MyJsonResult(ConstantCode.ERROR, reversResp.get("err_code_des"));
}else{return new MyJsonResult(ConstantCode.ERROR, resp.get("err_code_des"));
} 
  1. 申请退款(需要证书)
    1、创建map对象,添加请求参数。(appid,mch_id,nonce_str,sign,sign_type不用添加,reverse方法会自动将这些字段添加到参数中)添加到map中(注意查看参数要求!)。
    2、调用查询订单方法refund方法,根据返回参数判断退款请求。(注意查看返回参数!)。
    具体代码实现
public MyJsonResult refundPay(HttpServletRequest request,HttpServletResponse response) throws Exception {MyConfig config = new MyConfig();WXPay wxpay = new WXPay(config);       String total_fee=request.getParameter("total_fee");String out_trade_no=request.getParameter("out_trade_no");String refund_fee=request.getParameter("refund_fee");System.out.println(total_fee +"\n"+out_trade_no+"\n"+refund_fee); Map<String, String> data = new HashMap<String, String>();       //接收返回参数Map<String, String> resp = null;      data.put("total_fee", total_fee);data.put("out_trade_no", out_trade_no);data.put("refund_fee", refund_fee);//商户退款订单号data.put("out_refund_no",WXPayUtil.generateNonceStr().substring(0,11)); try {resp = wxpay.refund(data);           } catch (Exception e) {e.printStackTrace();}if(resp!=null){//请求成功 if("SUCCESS".equals(resp.get("return_code")) ){if("SUCCESS".equals(resp.get("result_code"))){return new MyJsonResult(ConstantCode.SUCCESS,"退款申请提交成功!");}return new MyJsonResult(ConstantCode.ERROR,resp.get("err_code_des"));}return new MyJsonResult(ConstantCode.ERROR,resp.get("return_msg"));}return new MyJsonResult(ConstantCode.ERROR,"位未知错误!");
}
  1. 退款查询(不需要证书)
    1、创建map对象,添加请求参数。(appid,mch_id,nonce_str,sign,sign_type不用添加,refundQuery方法会自动将这些字段添加到参数中)(注意查看参数要求!)。
    2、调用查询订单方法refundQuery方法,根据返回参数判断退款状态。(注意查看返回参数!)。
    参考代码
public MyJsonResult refundQuery(HttpServletRequest request,HttpServletResponse response) throws Exception {MyConfig config = new MyConfig();WXPay wxpay = new WXPay(config); String out_trade_no=request.getParameter("out_trade_no");//请求参数Map<String, String> data = new HashMap<String, String>(); //接收返回参数Map<String, String> resp = new HashMap<String, String>();data.put("out_trade_no", out_trade_no); try {resp = wxpay.refundQuery(data); } catch (Exception e) {e.printStackTrace();}if(resp!=null){if("SUCCESS".equals(resp.get("return_code")) ){if ("SUCCESS".equals(resp.get("result_code"))) {return new MyJsonResult(ConstantCode.SUCCESS,resp.get("refund_status_0"));}return new MyJsonResult(ConstantCode.ERROR, resp.get("err_code_des"));}return new MyJsonResult(ConstantCode.ERROR, resp.get("return_msg"));}return new MyJsonResult(ConstantCode.ERROR,"位未知错误!");		}

这篇关于后端微信刷卡支付功能实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/300813

相关文章

通俗易懂的Java常见限流算法具体实现

《通俗易懂的Java常见限流算法具体实现》:本文主要介绍Java常见限流算法具体实现的相关资料,包括漏桶算法、令牌桶算法、Nginx限流和Redis+Lua限流的实现原理和具体步骤,并比较了它们的... 目录一、漏桶算法1.漏桶算法的思想和原理2.具体实现二、令牌桶算法1.令牌桶算法流程:2.具体实现2.1

MySQL8.0设置redo缓存大小的实现

《MySQL8.0设置redo缓存大小的实现》本文主要在MySQL8.0.30及之后版本中使用innodb_redo_log_capacity参数在线更改redo缓存文件大小,下面就来介绍一下,具有一... mysql 8.0.30及之后版本可以使用innodb_redo_log_capacity参数来更改

C++使用栈实现括号匹配的代码详解

《C++使用栈实现括号匹配的代码详解》在编程中,括号匹配是一个常见问题,尤其是在处理数学表达式、编译器解析等任务时,栈是一种非常适合处理此类问题的数据结构,能够精确地管理括号的匹配问题,本文将通过C+... 目录引言问题描述代码讲解代码解析栈的状态表示测试总结引言在编程中,括号匹配是一个常见问题,尤其是在

Java实现检查多个时间段是否有重合

《Java实现检查多个时间段是否有重合》这篇文章主要为大家详细介绍了如何使用Java实现检查多个时间段是否有重合,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录流程概述步骤详解China编程步骤1:定义时间段类步骤2:添加时间段步骤3:检查时间段是否有重合步骤4:输出结果示例代码结语作

使用C++实现链表元素的反转

《使用C++实现链表元素的反转》反转链表是链表操作中一个经典的问题,也是面试中常见的考题,本文将从思路到实现一步步地讲解如何实现链表的反转,帮助初学者理解这一操作,我们将使用C++代码演示具体实现,同... 目录问题定义思路分析代码实现带头节点的链表代码讲解其他实现方式时间和空间复杂度分析总结问题定义给定

Java覆盖第三方jar包中的某一个类的实现方法

《Java覆盖第三方jar包中的某一个类的实现方法》在我们日常的开发中,经常需要使用第三方的jar包,有时候我们会发现第三方的jar包中的某一个类有问题,或者我们需要定制化修改其中的逻辑,那么应该如何... 目录一、需求描述二、示例描述三、操作步骤四、验证结果五、实现原理一、需求描述需求描述如下:需要在

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

python使用fastapi实现多语言国际化的操作指南

《python使用fastapi实现多语言国际化的操作指南》本文介绍了使用Python和FastAPI实现多语言国际化的操作指南,包括多语言架构技术栈、翻译管理、前端本地化、语言切换机制以及常见陷阱和... 目录多语言国际化实现指南项目多语言架构技术栈目录结构翻译工作流1. 翻译数据存储2. 翻译生成脚本

如何通过Python实现一个消息队列

《如何通过Python实现一个消息队列》这篇文章主要为大家详细介绍了如何通过Python实现一个简单的消息队列,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录如何通过 python 实现消息队列如何把 http 请求放在队列中执行1. 使用 queue.Queue 和 reque

Python如何实现PDF隐私信息检测

《Python如何实现PDF隐私信息检测》随着越来越多的个人信息以电子形式存储和传输,确保这些信息的安全至关重要,本文将介绍如何使用Python检测PDF文件中的隐私信息,需要的可以参考下... 目录项目背景技术栈代码解析功能说明运行结php果在当今,数据隐私保护变得尤为重要。随着越来越多的个人信息以电子形