Java 实现企业级支付

  • 一、简介
    • 1、支付方式
    • 2、微信支付
      • 支付方式的支付场景
      • 注册微信公众平台账号
      • 安装微信开发者工具
    • 3、支付宝支付
    • 4、银联支付
    • 5、开发工具与环境配置
  • 二、微信支付
    • 1、微信小程序支付
      • (1)开发小程序必备
      • (2)开通小程序支付业务
      • (3)小程序支付接口原理
      • (4)实现微信登陆小程序
        • 1)、为什么先创建小程序项目
        • 2)、设置小程序开发工具兼容 HBuilderX
        • 3)、小程序获取临时的登陆凭证
        • 4)、后端系统获取 openId
      • (5)创建小程序订单及创建支付订单
        • 1)、微信支付接口规则
        • 2)、支付订单的 API 参数
        • 3)、下载 SDK
        • 4)、创建支付订单
        • 5)、处理微信平台返回的结果
      • (6)商户系统接收支付结果
        • 1)、微信平台的支付回调
        • 2)、支付回调的参数(当return_code为SUCCESS时)
        • 3)、商户系统主动查询订单支付结果
      • 小结
    • 2、Native 支付
      • (1)什么是 Native 支付?
      • (2)开通 Native 支付
      • (3)Native 支付原理
      • (4)创建支付订单
        • 1)、创建支付订单提交的参数
        • 2)、创建支付订单
      • (5)生成支付二维码
      • (7)后端系统查询支付结果
    • 3、付款码支付
      • (1)什么是扫描支付
      • (2)没有扫码器怎么实现扫码付款
      • (3)扫码支付原理
      • (4)提交扣款请求需要的参数
      • (5)后端系统收款的方法
    • 4、JSAPI 支付
    • 5、H5 支付
    • 6、APP 支付
    • 7、刷脸支付
  • 三、支付宝支付
    • 1、支付宝小程序支付
    • 2、Native 支付
    • 3、H5 支付
  • 四、银联支付
    • 1、Native 支付
    • 2、H5 支付
  • 五、聚合支付
  • 总结

一、简介

1、支付方式

  目前主流的线上支付方式有:微信支付、支付宝支付、银联支付等。

2、微信支付

支付方式的支付场景

  1)、小程序支付场景
   用户在微信小程序上面下单支付,那么就调用微信小程序支付接口。

  2)、Native 支付场景
   用户在 PC 浏览器打开电商网站下单支付,此时电商网站调用微信的 Native 支付接口(PC 浏览器)。

  3)、付款码支付场景
   线下支付的时候,商家通过扫码器扫描你的付款码完成免密支付。

  4)、H5 支付场景
   用户在手机内置浏览器里打开电商网站下单支付,这个时候该网站调用的是微信的 H5 支付接口(手机浏览器)。

   付款码与收款码的区别:付款码可以接入商户系统,而收款码直接接入微信平台。

  5)、JASAPI 支付场景
   用户在微信内置浏览器里面打开电商网站下单支付,此时需要调用微信的 JSAPI 接口(必须时在微信内置的浏览器里)。

  6)、App 支付场景
   用户在第三方 APP 上面下单支付,此时就需要调用微信的 App 支付接口。

  7)、刷脸支付场景
   用户使用刷脸方式下单支付,此时就需要调用微信的刷脸支付接口。

注册微信公众平台账号

注册步骤:

  • 打开微信公众平台:https://mp.weixin.qq.com
  • 点击该页面右上角“立即注册”
  • 选择注册小程序
  • 填写用户注册信息
  • 其余注册部分后续补!!!

安装微信开发者工具

  微信开发者工具下载地址:https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html

  安装步骤:直接下一步!!!

3、支付宝支付

  后续待补!!!

4、银联支付

  后续待补!!!

5、开发工具与环境配置

  IDEA 版本:2020.1
    下载地址:https://www.jetbrains.com/idea/

  JDK 版本:1.8
    下载地址:https://www.oracle.com/java/technologies/javase-jdk16-downloads.html

  Maven 版本:3.6.3
    下载地址:https://maven.apache.org/download.cgi

  MySQL 版本:8.0.23
    下载地址:https://dev.mysql.com/downloads/mysql/

  数据库管理工具:Navicat Premium 1.5
    下载地址:http://www.navicat.com.cn/products/navicat-premium/

  Node 版本:15.12.0
    下载地址:https://nodejs.org/zh-cn/

  HBuilderX 版本:3.1.4
    下载(APP开发版)地址:https://www.dcloud.io/hbuilderx.html

  Postman 版本:1.8
    下载地址:https://www.postman.com/downloads/

二、微信支付

1、微信小程序支付

(1)开发小程序必备

  • 企业类型小程序唯一的 AppID 和 AppSecret;AppID 和 AppSecret 只有在实现支付的时候才会在后台代码中使用到
  • 开发者账户与小程序一一对应,每个开发者账户对应唯一的小程序,若要重新开发新的小程序就需要重新注册开发者账号
  • 除了AppID 和 AppSecret 之外,还需要从微信商户平台得到商户号、数字证书、密钥

(2)开通小程序支付业务

  后续待补!!!

(3)小程序支付接口原理

小程序支付交互图:

小程序支付业务流程:

  • ---------创建支付订单:---------
  • 用户在小程序上点击微信支付按钮。
  • 小程序通过发起 Ajax 请求 告诉商户系统向微信平台申请创建支付订单。
  • 商户系统检查小程序提交的数据,例如:openId 在数据库里是否存在,是否是用户用微信登陆小程序产生的 ID 值;还有就是小程序提交的订单编号是否有效。
  • 商户系统验证通过后,向微信平台发送生成支付订单的请求,并发送跟支付相关的各种信息,例如:订单金额?人民币还是其它币种?收款的商户ID?付款用户的 openId?
  • 微信平台接收到信息后核实是否有误,若是无误,则微信平台就会生成微信支付订单,并且将订单信息返回给商户系统。
  • 商户系统得到订单信息后,对订单信息生成 MD5 数字签名。
  • 然后商户系统把支付订单的参数返回给小程序。
  • -----------用户付款:-----------
  • 小程序将从商户系统得到的支付订单参数提交给微信平台进行验证,验证商户系统返回的参数是否正确(毕竟订单是商户系统提交给微信平台的,防止商户系统提交的信息与小程序给商户系统的不一致)。
  • 微信平台确认小程序的得到的支付参数没问题,于是就把商户创建的订单信息返回给小程序让用户确认;此时小程序就会弹出支付的金额和收款的商户信息。
  • 用户对弹出的信息没有异议后,于是输入支付密码。
  • 然后小程序向微信平台发送用户确认支付的请求。
  • 微信平台验证用户支付密码正确之后就可以执行支付订单了(微信平台从微信用户账户中扣款)。
  • 最后微信平台无论是扣款成功还是失败,都会把支付的结果分别发送给商户系统和小程序。

商户系统如何验证用户登陆:
  商户系统在申请创建支付订单之前,必须判断提交请求的小程序用户是否已经登陆,会不会是 Postman 或者 HttpClient 这类的软件模拟小程序发起的请求。
  商户系统验证用户是否登陆的方法:验证 openId 和 Token 字符串。用户在手机上用微信登陆小程序的时候,会产生一个唯一的 openId 值,商户系统会记录这个 openId 值,如果商户系统收到的请求里没有这个 openId 值或者这个 openId 值与数据库中的对应不上,就说明这不是一个合法用户;仅仅 openId 能核对还不行,还需要看发起请求的用户是否已经登陆小程序,只有用户登陆小程序之后,才可以证明是本人下单支付。成功登陆小程序的用户后端系统会返回一个 Token 字符串,小程序每次发器请求都会带上这个 Token 字符串,告诉后端系统已经登陆,后端系统也会验证 Token 字符串是否有效和是否过期。

(4)实现微信登陆小程序

1)、为什么先创建小程序项目

  因为后端项目需要拿到小程序提交的临时登陆凭证 code 字符串到微信平台验证,并且获得 open_id 字符串。

  用户在小程序点击登陆按钮;小程序获得临时登陆凭证和用户账号基本信息;小程序将得到的信息提交到商户系统;商户系统不信任小程序提交的信息,于是商户系统拿着登陆凭证及用户信息到微信平台去验证;如果微信平台核实登陆有效,就会向商户系统返回一个唯一的 openId 字符串;商户系统拿着 openId 字符串到本地数据库去查询,如果客户表里有 openId 的记录,则证明用户不是第一次登陆系统,然后执行正常登陆即可;如果客户表中查询不到 openId 字符串,则说明用户第一次登陆系统,于是后端系统将这些信息保存到数据表中,然后再执行登陆。登录成功商户系统给小程序返回一个登陆令牌字符串,以后小程序再发起请求都要带着这个令牌字符串,后端系统验证令牌字符串后,就知道小程序是登陆,如果小程序发起的请求没带令牌字符串或者令牌字符串不正确,后端系统则判定用户未登陆小程序。

2)、设置小程序开发工具兼容 HBuilderX

  • 微信扫描微信开发者工具二维码
  • 创建项目后,选择“设置”里的“安全设置”
  • 点击“安全”,开启“服务端口”
  • 打开 HBuilderX 工具,点击“工具”、“外部命令”、“运行设置”进入 HBuilderX 功能设置界面
  • 在“运行配置”里设置“微信开发者工具路径”
  • 然后新建一个 uni-app 项目

为什么使用 uni-app 框架?
  uni-app 是一个跨平台的移动端框架,它采用 Hybrid 方式开发,可以把代码编译成各种小程序、iOS App、Android App等;
  uni-app 可以使用 Vue 语法开发移动端程序;
  微信开发者工具语法提示不如 HBuilderX 智能。

  • 在 manifest.json 中的“微信小程序配置”设置小程序的 AppId

3)、小程序获取临时的登陆凭证

  • 用户使用微信账号登陆小程序,先要在微信 APP 上面获得临时登陆凭证
  • 在小程序端调用 uni.login() 即可获取登陆凭证字符串 code (code 的有效期为 5 分钟)
  • 调用 uni.getUserInfo() 方法获得微信用户基本信息(登陆按钮必须要有 open-type = “getUserInfo” 属性)
<view><button open-type="getUserInfo" @tap="login">登陆</button>
</view>methods: {login:function(){let that = thisuni.login({success:function(res){// console.log(res)let code = res.codeuni.getUserInfo({success:function(res){// console.log(res)let avatarUrl = res.userInfo.avatarUrllet nickName = res.userInfo.nickName// console.log(avatarUrl)// console.log(nickName)// 发起 Ajax 请求后端系统uni.request({url:that.url.wxLogin,method:"POST",data:{"code":code,"nickname":nickName,"photo":avatarUrl},// 回调函数:后端处理完 Ajax请求返回的结果success:function(res){// console.log(res)let token = res.data.tokenlet expire = res.data.expire// 因为小程序每次请求都需要带 token 字符串,// 于是将 token 保存到小程序本地StorageSync(同步)uni.setStorageSync("token",token)uni.setStorageSync("expire",expire)// 登陆成功后跳转页面uni.switchTab({url:"../index/index"})}})}})}})}
}

4)、后端系统获取 openId

商户向微信平台提交的请求参数如下:

参数名 含义 备注
appid 小程序ID
secret 小程序密钥
js_code 临时登陆凭证
grant_type 授权码模式 固定值为:authorization_code

@RestController
@RequestMapping("/app/wx")
public class WxController {@Value("${application.app-id}")private String appId;@Value("${application.app-secret}")private String appSecret;@Value("${application.key}")private String key;@Value("${application.mch-id}")private String mchId;@Autowiredprivate UserService userService;@Autowiredprivate OrderService orderService;@Autowiredprivate JwtUtils jwtUtils;@Autowiredprivate MyWxPayConfig myWxPayConfig;/*** 登录*/@PostMapping("/login")@ApiOperation("登录")public R login(@RequestBody WxLoginForm form){//表单校验ValidatorUtils.validateEntity(form);/*** 1、接收信息* 这部分是接收小程序端 Ajax请求传过来的用户信息*/Map map = new HashMap();map.put("appid",appId); //map.put("secret",appSecret); //map.put("js_code",form.getCode());map.put("grant_type","authorization_code");/*** 2、验证信息* 由于商户系统不信任小程序,于是将得到的信息拿到微信平台去验证* 如果微信平台验证通过会返回一个 openid 字符串*/// 微信平台 URLString url = "https://api.weixin.qq.com/sns/jscode2session";String response = HttpUtil.post(url,map);JSONObject jsonObject = JSONUtil.parseObj(response);String openId = jsonObject.getStr("openid");/*** 3、查询数据库* 商户系统拿着 openId 字符串到本地数据库去查询,* 如果客户表里有 openId 的记录,则证明用户不是第一次登陆系统,然后执行正常登陆即可;* 如果客户表中查询不到 openId 字符串,则说明用户第一次登陆系统,于是后端系统将这些信息保存到数据表中,然后再执行登陆。*/// 注册if (openId == null || openId.length() == 0){return R.error("临时登陆凭证不正确");}UserEntity userEntity = new UserEntity();userEntity.setOpenId(openId);QueryWrapper queryWrapper = new QueryWrapper(userEntity);int count = userService.count(queryWrapper);if (count == 0){userEntity.setNickname(form.getNickname());userEntity.setPhoto(form.getPhoto());userEntity.setType(2);  //1:普通用户登陆 2:微信用户登陆userEntity.setCreateTime(new Date());userService.save(userEntity);}// 登陆UserEntity user = new UserEntity();user.setOpenId(openId);QueryWrapper query = new QueryWrapper(user);UserEntity one = userService.getOne(query);Long userId = one.getUserId();/*** 4、返回登陆令牌* 登录成功商户系统给小程序返回一个登陆令牌字符串,* 以后小程序再发起请求都要带着这个令牌字符串,* 后端系统验证令牌字符串后,就知道小程序是登陆,* 如果小程序发起的请求没带令牌字符串或者令牌字符串不正确,后端系统则判定用户未登陆小程序。*///生成tokenString token = jwtUtils.generateToken(userId);
//        System.out.println(token);Map<String, Object> result = new HashMap<>();result.put("token", token);result.put("expire", jwtUtils.getExpire());return R.ok(result);}/*** 接收小程序已经支付成功的请求,修改订单状态码*/@Login@PostMapping("/updateOrderStatus")public R updateOrderStatus(UpdateOrderStatusForm form, @RequestHeader HashMap header){// 表单验证ValidatorUtils.validateEntity(form);String token = header.get("token").toString();long userId = Long.parseLong(jwtUtils.getClaimByToken(token).getSubject());int orderId = form.getOrderId();// 查询这个订单是否属于该用户OrderEntity orderEntity = new OrderEntity();orderEntity.setUserId((int) userId);orderEntity.setId(orderId);QueryWrapper wrapper = new QueryWrapper(orderEntity);int count = orderService.count(wrapper);// 验证逻辑的有效性if (count == 0){return R.error("用户与订单不匹配!");}/*** 如果匹配,商户系统需要发出网络请求到微信平台查询订单对应的结果*/// 根据小程序提交的商品订单ID查询商品订单的流水号orderEntity = orderService.getOne(wrapper);// 提取订单的流水号String code = orderEntity.getCode();Map map = new HashMap();map.put("appid",appId);map.put("mch_id",mchId);map.put("out_trade_no",code);map.put("nonce_str",WXPayUtil.generateNonceStr());try {// 生成数字签名String sign = WXPayUtil.generateSignature(map,key);map.put("sign",sign);WXPay wxPay = new WXPay(myWxPayConfig);// 到微信平台查询订单对应结果Map<String,String> result = wxPay.orderQuery(map);// 获取通信状态码和业务状态码String returnCode = result.get("return_code"); // 通信String resultCode = result.get("result_code"); // 业务// 这两个状态码都是SUCCESS表示小程序支付成功并且商户系统收到的小程序的支付成功通知不是由postman发出的if ("SUCCESS".equals(resultCode) && "SUCCESS".equals(returnCode)){//交易状态   trade_state// SUCCESS--支付成功//REFUND--转入退款//NOTPAY--未支付//CLOSED--已关闭//REVOKED--已撤销(刷卡支付)//USERPAYING--用户支付中//PAYERROR--支付失败(其他原因,如银行返回失败)//ACCEPT--已接收,等待扣款// 获得微信平台的交易状态String tradeState = result.get("trade_state");// 微信平台的交易状态为:SUCCESS 表示小程序的确支付成功,可以更改商户系统的状态码if ("SUCCESS".equals(tradeState)){UpdateWrapper updateWrapper = new UpdateWrapper();// 通过流水号(商户订单号)查询订单信息updateWrapper.eq("code",code);// 更改该条订单的支付状态码改为已付款// 状态:1未付款,2已付款,3已发货,4已签收updateWrapper.set("status",2);orderService.update(updateWrapper);return R.ok("订单状态已修改!");}else {return R.ok("订单状态未修改!");}}return R.ok("订单状态未修改!");} catch (Exception e) {e.printStackTrace();return R.error("查询支付订单失败!");}}
}

(5)创建小程序订单及创建支付订单

1)、微信支付接口规则

  • 为了保证交易的安全,支付必须使用 HTTPS 协议
  • 必须使用 POST 方式提交支付数据
  • 提交数据和返回结果都为 XML 格式
  • 所有数据采用 UTF-8 编码
  • 交易支付金额单位为 分,不能有小数点,并且最低金额为 1 元(1元=100)
  • 小程序的交易类型(trade_type)是 JSAPI
  • 境内的商户只能用 人民币交易

2)、支付订单的 API 参数

支付订单 API 参数文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1

必须上传的参数:

参数名称 参数名 是否必填 参数类型 示例 说明
公众账号ID appid String(32) wxd678efh567hg6787 微信支付分配的公众账号ID(企业号corpid即为此appId)
商户号 mch_id String(32) 1230000109 微信支付分配的商户号
随机字符串 nonce_str String(32) 5K8264ILTKCH16CQ2502SI8ZNMTM67VS 随机字符串,长度要求在32位以内。推荐随机数生成算法
签名 sign String(32) C380BEC2BFD727A4B6845133519F3AD6 通过签名算法计算得出的签名值
商品描述 body String(128) 会员充值 商品简单描述,该字段请按照规范传递,具体请见参数规定
商户订单号 out_trade_no String(32) 20150806125346 商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-* 且在同一个商户号下唯一
标价金额 total_fee Int 150 订单总金额,单位为分,详见支付金额
终端IP spbill_create_ip String(64) 123.12.12.123 支持IPV4和IPV6两种格式的IP地址。用户的客户端IP
通知地址 notify_url String(256) http://www.weixin.qq.com/wxpay/pay.php 异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数。
交易类型 trade_type String(16) JSAPI JSAPI -JSAPI支付、NATIVE -Native支付、APP -APP支付
用户标识 openid String(128) oUpF8uMuAJO_M2pxb1Q9zNjWeS6o trade_type=JSAPI时(即JSAPI支付),此参数必传,此参数为微信用户在商户对应appid下的唯一标识
设备号 device_info String(32) 013467007045764 自定义参数,可以为终端设备号(门店号或收银设备ID),PC网页或公众号内支付可以传"WEB"
签名类型 sign_type String(32) MD5 签名类型,默认为MD5,支持HMAC-SHA256和MD5。
商品详情 detail String(6000) 商品详细描述,对于使用单品优惠的商户,该字段必须按照规范上传
附加数据 attach String(127) 深圳分店 附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用。
标价币种 fee_type String(16) CNY 符合ISO 4217标准的三位字母代码,默认人民币:CNY,详细列表请参见货币类型
交易起始时间 time_start String(14) 20091225091010 订单生成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010
交易结束时间 time_expire String(14) 20091227091010 订单失效时间,格式为yyyyMMddHHmmss,如2009年12月27日9点10分10秒表示为20091227091010。订单失效时间是针对订单号而言的,由于在请求支付的时候有一个必传参数prepay_id只有两小时的有效期,所以在重入时间超过2小时的时候需要重新请求下单接口获取新的prepay_id。建议:最短失效时间间隔大于1分钟
订单优惠标记 goods_tag String(32) WXG 订单优惠标记,使用代金券或立减优惠功能时需要的参数,说明详见代金券或立减优惠
商品ID product_id String(32) 12235413214070356458058 trade_type=NATIVE时,此参数必传。此参数为二维码中包含的商品ID,商户自行定义。
指定支付方式 limit_pay String(32) no_credit 上传此参数no_credit–可限制用户不能使用信用卡支付
电子发票入口开放标识 receipt String(8) Y Y,传入Y时,支付成功消息和支付详情页将出现开票入口。需要在微信支付商户平台或微信公众平台开通电子发票功能,传此字段才可生效
是否需要分账 profit_sharing String(16) Y Y-是,需要分账N-否,不分账字母要求大写,不传默认不分账
场景信息 scene_info String(256) 该字段常用于线下活动时的场景信息上报,支持上报实际门店信息,商户也可以按需求自己上报相关信息。该字段为JSON对象数据

3)、下载 SDK

下载微信 SDK 地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=11_1

下载好SDK 后,将这个包解压,进入 src/main/java 目录下,然后将 “com”包复制到自己项目的 src/main/java 目录下,再然后修改“WXPay”中第 47 行的“SignType.HMACSHA256”改为“SignType.MD5”。

如果不去创建 WXPayConfig 的子类,就没有办法创建 WXPay 的对象,而WXPay 里有创建微信支付订单的方法,所以必须把 WXPayConfig 这个抽象类的子类创建出来。

package com.github.wxpay.sdk;import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;import javax.annotation.PostConstruct;
import java.io.*;@Component
public class MyWxPayConfig extends WXPayConfig {@Value("${application.app-id}")private String appId;@Value("${application.mch-id}")private String mchId;@Value("${application.key}")private String key;@Value("${application.cert-path}")private String certPath;// 用于保存数字证书的内容private byte[] certData;/*** 用于读书磁盘上的数字证书* 方法名可以随便取*  @PostConstruct 作用:当 Spring 创建完该对象并且完成各种依赖注入、值注入之后就要执行该方法*/@PostConstructpublic void init() throws Exception{File file = new File(certPath);FileInputStream fis = new FileInputStream(file);BufferedInputStream bis = new BufferedInputStream(fis);this.certData = new byte[(int) file.length()];bis.read(this.certData);bis.close();fis.close();}@OverrideString getAppID() {return appId;}@OverrideString getMchID() {return mchId;}@OverrideString getKey() {return key;}@OverrideInputStream getCertStream() {ByteArrayInputStream bais = new ByteArrayInputStream(this.certData);return bais;}@OverrideIWXPayDomain getWXPayDomain() {return new IWXPayDomain() {@Overridepublic void report(String domain, long elapsedTimeMillis, Exception ex) {}@Overridepublic DomainInfo getDomain(WXPayConfig config) {return new IWXPayDomain.DomainInfo(WXPayConstants.DOMAIN_API,true);}};}@Overridepublic int getHttpConnectTimeoutMs() {return super.getHttpConnectTimeoutMs();}@Overridepublic int getHttpReadTimeoutMs() {return super.getHttpReadTimeoutMs();}@Overridepublic boolean shouldAutoReport() {return super.shouldAutoReport();}@Overridepublic int getReportWorkerNum() {return super.getReportWorkerNum();}@Overridepublic int getReportQueueMaxSize() {return super.getReportQueueMaxSize();}@Overridepublic int getReportBatchSize() {return super.getReportBatchSize();}
}

4)、创建支付订单

<template><view class="page"><view class="order" v-for="list in lists" :key = "list"><view class="line-1"><text>订单号:{{list.code}}</text><text>{{list.status}}</text></view><view class="line-2"><text>订单的简要信息!!!</text></view><view class="line-3"><text>金额:{{list.amount}} 元</text><button class="pay-btn" type="primary" v-if="list.status == '未付款' " @tap="pay(list.id)">付款</button></view></view></view>
</template><script>export default {data() {return {lists:[]}},methods: {pay:function(id){let that = thisuni.request({url:that.url.microAppPayOrder,method:"POST",header:{"token":uni.getStorageSync("token")},data:{"orderId": id},success:function(res){// console.log(res)let timeStamp = res.data.timeStamplet nonceStr = res.data.nonceStrlet pack = res.data.packagelet paySign = res.data.paySignuni.requestPayment({"timeStamp":timeStamp,"nonceStr":nonceStr,"package":pack,"signType":"MD5","paySign":paySign,success() {uni.showToast({title:"支付成功"})// 小程序发出请求让后端系统主动查询支付结果},fail() {uni.showToast({title:"支付失败"})}})}})}},onShow() {let that = thisuni.request({url:that.url.searchUserOrderList,method:"POST",header:{"token":uni.getStorageSync("token")},data:{"page": 1,"length": 20},success(res) {console.log(res)let list = res.data.listfor(let one of list){if(one.status == 1){one.status = "未付款"}else if(one.status == 2){one.status == "已付款"}else if(one.status == 3){one.status == "已发货"}else if(one.status == 4){one.status == "已签收"}}that.lists = list}})}}
</script><style>.page{padding: 10px;}.order{padding: 10px;border-bottom: solid 1px #e0e0e0;}.line-1{display: flex;justify-content: space-between;padding-bottom: 5px;}.line-2{padding-bottom: 5px;}.line-3{display: flex;justify-content: space-between;align-items: baseline;}.pay-btn{margin: 0;font-size: 14px;line-height: 2;}</style>

5)、处理微信平台返回的结果

微信平台响应的参数:

参数名称 参数名 说明
状态码 return_code 状态码有两种值:success、fail 该状态码只代表商户的请求是否成功送达微信平台,至于微信平台能不能成功创建支付订单,状态码反映不出来
小程序ID app_id 返回商户上传的 app_id
商户ID mch_id 返回商户上传的 mch_id
随机字符串 nonce_str 微信平台返回的一个随机字符串
数字签名 sign 微信平台将返回的数据进行数字签名,防止发送数据时丢包,商户收到数据之后进行MD5运算,然后跟数字签名比较,如果相同则数据没有丢包
业务结果 result_code success:表示支付订单创建成功,fail:表示支付订单创建失败
交易类型 trade_type 原封不动返回提交的交易类型
支付订单ID prepay_id 微信平台产生的订单ID

后台商户系统端:

    /*** 小程序付款*/@Login@PostMapping("/microAppPayOrder")public R microAppPayOrder(PayOrderForm form, @RequestHeader HashMap header){/*** 1、小程序申请创建支付订单* 商户系统校验小程序提供的信息是否无误*/// 校验ValidatorUtils.validateEntity(form);String token = header.get("token").toString();long userId = Long.parseLong(jwtUtils.getClaimByToken(token).getSubject());int orderId = form.getOrderId();UserEntity user = new UserEntity();user.setUserId(userId);QueryWrapper wrapper = new QueryWrapper(user);long count = userService.count(wrapper);if (count == 0){return R.error("用户不存在!");}// 定义一个变量用于保存用户的 openIdString openId = userService.getOne(wrapper).getOpenId();OrderEntity order = new OrderEntity();order.setUserId((int) userId);order.setId(orderId);order.setStatus(1);wrapper = new QueryWrapper(order);count = orderService.count(wrapper);if (count == 0){return R.error("不是有效订单!");}// 验证购物券是否有效// 验证团购是否有效order = new OrderEntity();order.setId(orderId);wrapper = new QueryWrapper(order);order = orderService.getOne(wrapper);/*** 2、商户平台校验信息无误后,利用 SDK程序包 向微信平台发出创建支付订单请求*/// 获取订单的总金额 乘100(单位转为分) 取整(有小数点后两位)String amount = order.getAmount().multiply(new BigDecimal("100")).intValue() + "";// 获取用户 openidtry {WXPay wxPay = new WXPay(myWxPayConfig);Map map = new HashMap();// 随机字符串,可自己编写代码生成,此处直接使用SDK产生map.put("nonce_str", WXPayUtil.generateNonceStr());// 商品描述map.put("body", "订单备注");// 商户订单号(编号为字符串)map.put("out_trade_no", order.getCode());// 标价金额map.put("total_fee", amount);// 终端IPmap.put("spbill_create_ip", "127.0.0.1");// 支付结果通知地址,地址可以随便编写,即使商户系统接收不到付款通知结果,也可以让商户平台主动向微信平台发起请求去得到支付结果map.put("notify_url", "https://127.0.0.1/test");// 交易类型,固定为:JSAPImap.put("trade_type", "JSAPI");// 用户标识map.put("openid", openId);// 上面是必填,下面是根据场景填/*// 设备号map.put("device_info", );// 签名类型map.put("sign_type", );// 商品详情map.put("detail", );// 剩余参数根据具体情况补充,详情参考API文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1*//*** 此方法可以帮我们发出请求让微信平台生成支付订单*/Map<String,String> result = wxPay.unifiedOrder(map);// 微信支付订单的IDString prepayId = result.get("prepay_id");// 将支付订单ID保存到数据表order.setPrepayId(prepayId);if (prepayId != null){UpdateWrapper updateWrapper = new UpdateWrapper();updateWrapper.eq("id",order.getId());orderService.update(order,updateWrapper);/*** 将订单数据返回给小程序*/// 生成数字签名:由于小程序端没有微信提供的sdk程序包,如果自己写需要写很多代码,// 所以不如就让后台程序生成数字签名,返回给小程序map.clear();map.put("appId",appId);// 时间戳String timeStamp = new Date().getTime() + "";map.put("timeStamp",timeStamp);// 随机字符串String nonceStr = WXPayUtil.generateNonceStr();map.put("nonceStr",nonceStr);map.put("package","prepay_id =" + prepayId);map.put("signType","MD5");// 生成数字签名String paySign = WXPayUtil.generateSignature(map,key);/*** 3、商户系统将从微信平台得到的信息MD5签名后,将数据返回给小程序*/return R.ok().put("package","prepay_id =" + prepayId).put("timeStamp",timeStamp).put("nonceStr",nonceStr).put("paySign",paySign);}else {return R.error("支付订单创建失败!");}} catch (Exception e) {e.printStackTrace();return R.error("微信支付模块故障");}}/*** 商户系统web接收支付结果通知* @param request* @param response* @throws Exception*/@RequestMapping("/recieveMessage")public void recieveMessage(HttpServletRequest request, HttpServletResponse response) throws Exception {// 设置字符集request.setCharacterEncoding("utf-8");// 创建字符流读取请求里的数据Reader reader = request.getReader();BufferedReader br = new BufferedReader(reader);String line = br.readLine();StringBuffer temp = new StringBuffer();while (line != null){temp.append(line);line = br.readLine();}br.close();reader.close();/****/Map<String, String> map = WXPayUtil.xmlToMap(temp.toString());String resultCode = map.get("result_code");String returnCode = map.get("return_code");if ("SUCCESS".equals(resultCode) && "SUCCESS".equals(returnCode)){// 如果通信状态码和业务状态码都是:SUCCESS// 取出商品订单流水号String outTradeNo = map.get("out_trade_no");UpdateWrapper updateWrapper = new UpdateWrapper();// 查询条件updateWrapper.eq("code",outTradeNo);// 设置修改哪个字段的值updateWrapper.set("status",2);orderService.update(updateWrapper);// 设置响应的字符集response.setCharacterEncoding("utf-8");// 告诉微信平台响应的内容是什么response.setContentType("application/xml");Writer writer = response.getWriter();BufferedWriter bufferedWriter = new BufferedWriter(writer);bufferedWriter.write("<xml>\n" +"  <return_code><![CDATA[SUCCESS]]></return_code>\n" +"  <return_msg><![CDATA[OK]]></return_msg>\n" +"</xml>");bufferedWriter.close();writer.close();}}

(6)商户系统接收支付结果

1)、微信平台的支付回调

支付结果通知文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7&index=8

  • 微信平台会把用户支付的结果,发送到 URL 回调地址(即:notify_url),格式为 XML
  • 如果商户系统没有响应,微信平台会每隔一段时间再发送一次支付结果;多次未响应之后就不再发送

2)、支付回调的参数(当return_code为SUCCESS时)

参数名称 参数名 是否必填 参数类型 示例 说明
通信状态码 return_code String SUCCESS/FAIL 通信成功不代表支付成功,例如:微信平台从银行卡扣款,由于余额不足,扣款不成功,但小程序发送给微信平台的扣款请求微信平台收到了,所以通信状态码为SUCCESS
业务状态码 result_code String(16) SUCCESS SUCCESS表示支付成功
微信支付订单号 transaction_id String(32) 1217752501201407033233368018 微信支付订单号
商户订单号 out_trade_no String(32) 1212321211201407033568112322 商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-*@ ,且在同一个商户号下唯一
订单金额 total_fee Int 100 订单总金额,单位为分
小程序ID appid String(32) wx8888888888888888 微信分配的小程序ID
用户标识 openid String(128) wxd930ea5d5a258f4f 用户在商户appid下的唯一标识
商户号 mch_id String(32) 1900000109 微信支付分配的商户号
随机字符串 nonce_str String(32) 5K8264ILTKCH16CQ2502SI8ZNMTM67VS 随机字符串,不长于32位
签名 sign String(32) C380BEC2BFD727A4B6845133519F3AD6 签名,详见签名算法
交易类型 trade_type String(16) JSAPI JSAPI、NATIVE、APP
现金支付金额 cash_fee Int 100 现金支付金额订单现金支付金额,详见支付金额
付款银行 bank_type String(32) CMC 银行类型,采用字符串类型的银行标识,银行类型见银行列表
是否关注公众账号 is_subscribe String(1) Y 用户是否关注公众账号,Y-关注,N-未关注
支付完成时间 time_end String(14) 20141030133525 支付完成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010
设备号 device_info String(32) 013467007045764 微信支付分配的终端设备号
签名类型 sign_type String(32) HMAC-SHA256 签名类型,目前支持HMAC-SHA256和MD5,默认为MD5
错误代码 err_code String(32) SYSTEMERROR 错误返回的信息描述
错误代码描述 err_code_des String(128) 系统错误 错误返回的信息描述
应结订单金额 settlement_total_fee Int 100 应结订单金额=订单金额-非充值代金券金额,应结订单金额<=订单金额。
货币种类 fee_type String(8) CNY 货币类型,符合ISO4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
现金支付货币类型 cash_fee_type String(16) CNY 货币类型,符合ISO4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
总代金券金额 coupon_fee Int 10 代金券金额<=订单金额,订单金额-代金券金额=现金支付金额,详见支付金额
代金券使用数量 coupon_count Int 1 代金券使用数量
代金券类型 coupon_type_$n String CASH CASH–充值代金券 NO_CASH—非充值代金券并且订单使用了免充值券后有返回(取值:CASH、NO_CASH)。$n为下标,该笔订单使用多张代金券时,从0开始编号,举例:coupon_type_0、coupon_type_1注意:只有下单时订单使用了优惠,回调通知才会返回券信息。下列情况可能导致订单不可以享受优惠:可能情况。
代金券ID coupon_id_$n String(20) 10000 代金券ID,$n为下标,该笔订单使用多张代金券时,从0开始编号,举例:coupon_id_0、coupon_id_1注意:只有下单时订单使用了优惠,回调通知才会返回券信息。下列情况可能导致订单不可以享受优惠:可能情况。
单个代金券支付金额 coupon_fee_$n Int 100 单个代金券支付金额,$n为下标,从0开始编号
商家数据包 attach String(128) 123456 商家数据包,原样返回

3)、商户系统主动查询订单支付结果

为什么需要商户系统主动查询支付结果?

  • 即使在外网环境下,也不能保证商户系统一定能接收到微信支付结果的通知消息
  • 如果微信平台出现故障,有可能支付成功后,没有发送支付通知给商户系统
  • 无论是哪一端出现网络故障,都能导致商户系统接收不到支付结果的通知

商户系统什么时候主动查询支付结果?

  • 当小程序上面提示支付成功后,小程序应当立即向商户系统发送Ajax请求,告诉商户系统小程序端已经收到付款成功的通知,商户系统也应该去微信平台查询订单的支付结果。

商户系统向微信平台发送网络请求需要的参数:
查询订单文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_2

参数名称 参数名 是否必填 参数类型 示例 说明
小程序ID appid String(32) wx8888888888888888 微信分配的小程序ID
商户号 mch_id String(32) 1900000109 微信支付分配的商户号
微信支付订单号(与商户订单号二选一) transaction_id String(32) 1217752501201407033233368018 微信支付订单号
商户订单号(与支付订单号二选一) out_trade_no String(32) 1212321211201407033568112322 商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-*@ ,且在同一个商户号下唯一
随机字符串 nonce_str String(32) 5K8264ILTKCH16CQ2502SI8ZNMTM67VS 随机字符串,不长于32位
签名 sign String(32) C380BEC2BFD727A4B6845133519F3AD6 签名,详见签名算法

小程序端:
这段代码为“创建支付订单”小程序端的补充。

// 小程序发出请求让后端系统主动查询支付结果
uni.request({url:that.url.updateOrderStatus,method:"POST",header:{"token":uni.getStorageSync("token")},data:{"orderId": id},success() {// 这是数组let pages = getCurrentPages()// 从当前数组中取出最后一个元素let page = pages[pages.length - 1]page.onShow()},fail() {// 选择控制台输出而不是showToast方式// 原因是用户付款成功了,就不必给用户提供异常信息了console.log("更新订单状态失败!")}
})

商户系统端:

/*** 接收小程序已经支付成功的请求,修改订单状态码*/@Login@PostMapping("/updateOrderStatus")public R updateOrderStatus(UpdateOrderStatusForm form, @RequestHeader HashMap header){// 表单验证ValidatorUtils.validateEntity(form);String token = header.get("token").toString();long userId = Long.parseLong(jwtUtils.getClaimByToken(token).getSubject());int orderId = form.getOrderId();// 查询这个订单是否属于该用户OrderEntity orderEntity = new OrderEntity();orderEntity.setUserId((int) userId);orderEntity.setId(orderId);QueryWrapper wrapper = new QueryWrapper(orderEntity);int count = orderService.count(wrapper);// 验证逻辑的有效性if (count == 0){return R.error("用户与订单不匹配!");}/*** 如果匹配,商户系统需要发出网络请求到微信平台查询订单对应的结果*/// 根据小程序提交的商品订单ID查询商品订单的流水号orderEntity = orderService.getOne(wrapper);// 提取订单的流水号String code = orderEntity.getCode();Map map = new HashMap();map.put("appid",appId);map.put("mch_id",mchId);map.put("out_trade_no",code);map.put("nonce_str",WXPayUtil.generateNonceStr());try {// 生成数字签名String sign = WXPayUtil.generateSignature(map,key);map.put("sign",sign);WXPay wxPay = new WXPay(myWxPayConfig);// 到微信平台查询订单对应结果Map<String,String> result = wxPay.orderQuery(map);// 获取通信状态码和业务状态码String returnCode = result.get("return_code"); // 通信String resultCode = result.get("result_code"); // 业务// 这两个状态码都是SUCCESS表示小程序支付成功并且商户系统收到的小程序的支付成功通知不是由postman发出的if ("SUCCESS".equals(resultCode) && "SUCCESS".equals(returnCode)){//交易状态 trade_state// SUCCESS--支付成功//REFUND--转入退款//NOTPAY--未支付//CLOSED--已关闭//REVOKED--已撤销(刷卡支付)//USERPAYING--用户支付中//PAYERROR--支付失败(其他原因,如银行返回失败)//ACCEPT--已接收,等待扣款// 获得微信平台的交易状态String tradeState = result.get("trade_state");// 微信平台的交易状态为:SUCCESS 表示小程序的确支付成功,可以更改商户系统的状态码if ("SUCCESS".equals(tradeState)){UpdateWrapper updateWrapper = new UpdateWrapper();// 通过流水号(商户订单号)查询订单信息updateWrapper.eq("code",code);// 更改该条订单的支付状态码改为已付款// 状态:1未付款,2已付款,3已发货,4已签收updateWrapper.set("status",2);orderService.update(updateWrapper);return R.ok("订单状态已修改!");}else {return R.ok("订单状态未修改!");}}return R.ok("订单状态未修改!");} catch (Exception e) {e.printStackTrace();return R.error("查询支付订单失败!");}}

小结

2、Native 支付

(1)什么是 Native 支付?

   用户用手机微信扫描 PC 浏览器打开电商网站支付二维码支付,此时电商网站调用微信的 Native 支付接口(PC 浏览器)。

(2)开通 Native 支付

   后续待补!!!

(3)Native 支付原理

微信Native 支付开发文档:https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_7_2.shtml

支付时序图:

  后续待补!!!

支付业务流程:

  • ---------创建支付订单:---------
  • 用户在网页上面点击支付按钮。
  • 网页向后端系统发送 Ajax请求,并且提交商品订单号、用户的 Token 令牌字符串。
  • 后端系统验证网页端的数据之后,让微信平台创建支付订单,并且提交商户号、appId、商户订单号、随机字符串、数字签名等信息。
  • 微信平台验证之后,创建支付订单,并将支付订单的相关信息返回给商户系统。
  • 商户系统从接收到的信息里提取支付链接,并把支付链接生成二维码图片。
  • 然后商户系统把这个二维码图片的URL地址返回给网页,网页端处理后就可以展现支付二维码。
  • -----------用户付款:-----------
  • 用户用微信扫描二维码,与此同时,用户微信app会向微信平台发出请求查询商户创建的支付订单。
  • 微信平台弹出对话框显示哪个商户创建的支付订单和订单金额。
  • 用户确认商户和金额没问题之后,就在手机上输入密码,于是这个付款请求就会被微信app发送给微信平台。
  • 微信平台验证通过后,就执行扣款。
  • 扣款成功后将支付结果分别发送给商户系统和用户微信app。

(4)创建支付订单

1)、创建支付订单提交的参数

微信 Native 支付支付订单参数文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_4_1.shtml

参数名称 参数名 是否必填 参数类型 示例 说明
应用ID appid string[1,32] wxd678efh567hg6787 body 直连商户申请的公众号或移动应用appid。
商户号 mchid string[1,32] 1230000109 body 直连商户的商户号,由微信支付生成并下发。
随机字符串 nonce_str String 随机字符串
数字签名 sign String
商品描述 body String qq会员
商户订单号 out_trade_no string[6,32] 1217752501201407033233368018 body 商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一
订单金额 total_fee int 100 订单金额,单位为分。
用户终端IP apbill_create_ip string[1,45] 14.23.150.211 用户的客户端IP,支持IPv4和IPv6两种格式的IP地址。
通知地址 notify_url string[1,256] https://www.weixin.qq.com/wxpay/pay.php body 通知URL必须为直接可访问的URL,不允许携带查询串。 格式:URL
交易类型 trade_type string NATIVE

2)、创建支付订单

    /*** Native 支付*/@Login@PostMapping("/nativePayOrder")@ApiOperation("Native 付款")public R nativePayOrder(PayOrderForm form, @RequestHeader HashMap header){/*** 1、用户点击支付,网页发送Ajax请求向商户系统申请创建支付订单* 商户系统校验网页提供的信息是否无误*/// 校验ValidatorUtils.validateEntity(form);String token = header.get("token").toString();long userId = Long.parseLong(jwtUtils.getClaimByToken(token).getSubject());int orderId = form.getOrderId();UserEntity user = new UserEntity();user.setUserId(userId);QueryWrapper wrapper = new QueryWrapper(user);long count = userService.count(wrapper);if (count == 0){return R.error("用户不存在!");}OrderEntity order = new OrderEntity();order.setUserId((int) userId);order.setId(orderId);order.setStatus(1);wrapper = new QueryWrapper(order);count = orderService.count(wrapper);if (count == 0){return R.error("不是有效订单!");}// 验证购物券是否有效// 验证团购是否有效order = new OrderEntity();order.setId(orderId);wrapper = new QueryWrapper(order);order = orderService.getOne(wrapper);/*** 2、商户平台校验信息无误后,利用 SDK程序包 向微信平台发出创建支付订单请求*/// 获取订单的总金额 乘100(单位转为分) 取整(有小数点后两位)String amount = order.getAmount().multiply(new BigDecimal("100")).intValue() + "";// 获取用户 openidtry {WXPay wxPay = new WXPay(myWxPayConfig);Map map = new HashMap();// 随机字符串,可自己编写代码生成,此处直接使用SDK产生map.put("nonce_str", WXPayUtil.generateNonceStr());// 商品描述map.put("body", "订单备注");// 商户订单号(编号为字符串)map.put("out_trade_no", order.getCode());// 标价金额map.put("total_fee", amount);// 终端IPmap.put("spbill_create_ip", "127.0.0.1");// 支付结果通知地址,地址可以随便编写,即使商户系统接收不到付款通知结果,也可以让商户平台主动向微信平台发起请求去得到支付结果map.put("notify_url", "https://127.0.0.1/test");// 交易类型,固定为:NATIVEmap.put("trade_type", "NATIVE");// WXPay 对象会帮我们生成数字签名// 我们也可以自己生成数字签名传到微信平台String sign = WXPayUtil.generateSignature(map,key);map.put("sign",sign);/*** 3、微信平台校验信息无误后生成支付订单** 此方法可以帮我们发出请求让微信平台生成支付订单*/Map<String,String> result = wxPay.unifiedOrder(map);// 获取微信支付订单的IDString prepayId = result.get("prepay_id");// 获取支付链接的URLString codeUrl = result.get("code_url");if (prepayId != null){// 将支付订单ID保存到数据表order.setPrepayId(prepayId);UpdateWrapper updateWrapper = new UpdateWrapper();updateWrapper.eq("id",order.getId());orderService.update(order,updateWrapper);/*** 4、商户系统将订单数据返回给网页* 网页端通过将支付链接转换为二维码图片展示给用户*/return R.ok().put("codeUrl", codeUrl);}else {return R.error("支付订单创建失败!");}} catch (Exception e) {e.printStackTrace();return R.error("微信支付模块故障");}}

(5)生成支付二维码

  hutool 工具包最新版内置了生成二维码的工具类,只需引入依赖,同时还需引入下面一个依赖,因为 hutool 在生成二维码图片时依赖下面那个包:

        <dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.3.1</version></dependency><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.4.1</version></dependency>

后端生成二维码图片:

/*** 将支付链接转化为二维码图片* @param request* @param response*/@GetMapping("/qrcode")public void qrcode(HttpServletRequest request, HttpServletResponse response) throws IOException {// 从请求中获取支付链接String codeUrl = request.getParameter("codeUrl");if (codeUrl != null && codeUrl.length() > 0){// 设置图片的大小QrConfig qrConfig = new QrConfig();qrConfig.setWidth(250); // 设置图片的宽qrConfig.setHeight(250);// 设置图片的高qrConfig.setMargin(2); // 设置二维码图片边块的宽度// 通过IO流的方式将图片写到响应里面OutputStream outputStream = response.getOutputStream();/*** 生成二维码图片* codeUrl:支付链接* qrConfig:图片配置信息* jpg:生成图片的格式(也可以为其它格式)* outputStream:二维码图片的位置,将二维码图片写到响应里*/QrCodeUtil.generate(codeUrl,qrConfig,"jpg",outputStream);outputStream.close();}}

(7)后端系统查询支付结果

  用户在微信上付款成功后可以收到付款结果通知,但由于网页端和微信app是不同的平台,网页端无法收到付款成功的通知,因此网页端可以用定时器的方式发送Ajax请求让后端系统主动查询支付结果,然后返回给网页端(例如:5秒发一次,发10次)。

/*** 查询支付订单状态并修改商品订单状态* @return*/@Login@PostMapping("/searchOrderStatus")@ApiOperation("查询支付订单状态")public R searchOrderStatus(SearchOrderStatusForm form, @RequestHeader HashMap header){// 表单验证ValidatorUtils.validateEntity(form);String token = header.get("token").toString();long userId = Long.parseLong(jwtUtils.getClaimByToken(token).getSubject());int orderId = form.getOrderId();// 查询这个订单是否属于该用户OrderEntity orderEntity = new OrderEntity();orderEntity.setUserId((int) userId);orderEntity.setId(orderId);QueryWrapper wrapper = new QueryWrapper(orderEntity);int count = orderService.count(wrapper);// 验证逻辑的有效性if (count == 0){return R.error("用户与订单不匹配!");}/*** 如果匹配,商户系统需要发出网络请求到微信平台查询订单对应的结果*/// 根据小程序提交的商品订单ID查询商品订单的流水号orderEntity = orderService.getOne(wrapper);// 提取订单的流水号String code = orderEntity.getCode();Map map = new HashMap();map.put("appid",appId);map.put("mch_id",mchId);map.put("out_trade_no",code);map.put("nonce_str",WXPayUtil.generateNonceStr());try {// 生成数字签名String sign = WXPayUtil.generateSignature(map,key);map.put("sign",sign);WXPay wxPay = new WXPay(myWxPayConfig);// 到微信平台 查询订单对应结果Map<String,String> result = wxPay.orderQuery(map);// 获取通信状态码和业务状态码String returnCode = result.get("return_code"); // 通信String resultCode = result.get("result_code"); // 业务// 这两个状态码都是SUCCESS表示小程序支付成功并且商户系统收到的小程序的支付成功通知不是由postman发出的if ("SUCCESS".equals(resultCode) && "SUCCESS".equals(returnCode)){//交易状态  trade_state// SUCCESS--支付成功//REFUND--转入退款//NOTPAY--未支付//CLOSED--已关闭//REVOKED--已撤销(刷卡支付)//USERPAYING--用户支付中//PAYERROR--支付失败(其他原因,如银行返回失败)//ACCEPT--已接收,等待扣款// 获得微信平台的交易状态String tradeState = result.get("trade_state");// 微信平台的交易状态为:SUCCESS 表示小程序的确支付成功,可以更改商户系统的状态码if ("SUCCESS".equals(tradeState)){UpdateWrapper updateWrapper = new UpdateWrapper();// 通过流水号(商户订单号)查询订单信息updateWrapper.eq("code",code);// 更改该条订单的支付状态码改为已付款// 状态:1未付款,2已付款,3已发货,4已签收updateWrapper.set("status",2);// 设置支付方式:1微信,2支付宝,3银联updateWrapper.set("payment_type",1);// 更新商品订单状态orderService.update(updateWrapper);return R.ok("订单状态已修改!");}else {return R.ok("订单状态未修改!");}}return R.ok("订单状态未修改!");} catch (Exception e) {e.printStackTrace();return R.error("查询支付订单失败!");}}

3、付款码支付

(1)什么是扫描支付

  线下支付的时候,商家通过扫码器扫描你的付款码完成免密支付,这个免密支付并不是真正意义上的免密支付,在一段时间内使用一次以上的付款码支付,第二次还是需要输入密码。

(2)没有扫码器怎么实现扫码付款

  扫码器的作用相当于键盘,它能识别一维码或二维码的内容,然后输入到电脑。在测试开发时,可以对结账页面添加键盘监听器模拟扫码输入。

(3)扫码支付原理

微信扫码支付文档:https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=5_4
扫码支付时序图:

扫码支付业务流程:

  • 收银员扫描商品并在商户收银系统生成支付订单,然后向用户展示支付金额。
  • 用户打开微信app出示付款码。
  • 收银员使用扫码设备读取用户付款码,并将读取的信息上传收银系统。
  • 收银系统得到扫码数据后,向后端系统发起支付请求。
  • 后端系统得到信息后,将其和其它数据一起发送到微信平台。
  • 微信平台验证过数据后,生成支付订单并扣款。
  • 然后微信平台将支付结果返回给后端系统和用户微信app。
  • 后端系统收到支付结果后,如果支付成功,后端系统更改订单状态,然后将结果进行签名验证,返回给收银系统。
  • 收银员看到收银系统的成功支付信息后给用户商品。

(4)提交扣款请求需要的参数

微信付款码支付订单参数文档:https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_10&index=1

参数名称 参数名 是否必填 参数类型 示例 说明
公众账号ID appid String(32) wx8888888888888888 微信分配的公众账号ID(企业号corpid即为此appId)
商户号 mch_id String(32) 1900000109 微信支付分配的商户号
随机字符串 nonce_str String(32) 5K8264ILTKCH16CQ2502SI8ZNMTM67VS 随机字符串,不长于32位。推荐随机数生成算法
签名 sign String(32) C380BEC2BFD727A4B6845133519F3AD6 签名,详见签名生成算法
商品描述 body String(128) QQ公仔 商品简单描述,该字段须严格按照规范传递,具体请见参数规定
商户订单号 out_trade_no String(32) 1217752501201407033233368018 商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-
订单金额 total_fee Int 100 订单总金额,单位为分,只能为整数,详见支付金额
终端IP spbill_create_ip String(64) 127.0.0.1 支持IPV4和IPV6两种格式的IP地址。调用微信支付API的机器IP
付款码 auth_code String(128) 120061098828009406 扫码支付付款码,设备读取用户微信中的条码或者二维码信息(注:用户付款码条形码规则:18位纯数字,以10、11、12、13、14、15开头)
签名类型 sign_type String(32) HMAC-SHA256 签名类型,目前支持HMAC-SHA256和MD5,默认为MD5
设备号 device_info String(32) 013467007045764 终端设备号(商户自定义,如门店编号)
商品详情 detail String(6000) 单品优惠功能字段,需要接入详见单品优惠详细说明
附加数据 attach String(127) 说明 附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据
货币类型 fee_type String(16) CNY 符合ISO4217标准的三位字母代码,默认人民币:CNY,详见货币类型
订单优惠标记 goods_tag String(32) 1234 订单优惠标记,代金券或立减优惠功能的参数,详见代金券或立减优惠
指定支付方式 limit_pay String(32) no_credit no_credit–指定不能使用信用卡支付
交易起始时间 time_start String(14) 20091225091010 订单生成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010
交易结束时间 time_expire String(14) 20091227091010 订单失效时间,格式为yyyyMMddHHmmss,如2009年12月27日9点10分10秒表示为20091227091010。注意:最短失效时间间隔需大于1分钟
电子发票入口开放标识 receipt String(8) Y Y,传入Y时,支付成功消息和支付详情页将出现开票入口。需要在微信支付商户平台或微信公众平台开通电子发票功能,传此字段才可生效

(5)后端系统收款的方法


4、JSAPI 支付

5、H5 支付

6、APP 支付

  后续待补!!!

7、刷脸支付

  后续待补!!!

三、支付宝支付

1、支付宝小程序支付

  后续待补!!!

2、Native 支付

  后续待补!!!

3、H5 支付

  后续待补!!!

四、银联支付

1、Native 支付

  后续待补!!!

2、H5 支付

  后续待补!!!

五、聚合支付

  后续待补!!!


总结

该项目文档:

Java 实现企业级支付相关推荐

  1. 手把手带你开发企业级支付系统

    499元特惠价 原价 1599元 的 <企业级支付系统从0到1手把手开发实战> === 课程内容 === 支付项目从0带你手把手落地,包含了完整的商品.会员.订单等多个模块,在进行支付宝沙 ...

  2. 《Java EE企业级应用开发》,《分布式爬虫》等书包邮送50本!企业开发利器!...

    来给大家送一波福利,这次联系了10个好友一起给各位送书,每个号送 5 本,一共 50本,还包邮哦. 感谢传智播客对本次活动的赞助.   金主介绍:传智播客是国内数一数二的IT培训机构,现在关注传智播客 ...

  3. JAVA对接支付宝支付(超详细,一看就懂)

    Java对接支付宝支付 更多内容 冷文博客: 传送门 引入 为什么要发这篇帖子呢?原因很简单,就是因为在一个稍稍正规一点的应用上都会有支付这个环节,我们日常的在线支付如今包括支付宝,微信钱包,QQ钱包 ...

  4. Java集成PayPal支付

    Java集成PayPal支付 1.申请账号 浏览器中输入:https://www.paypal.com,点击 "注册" 选择 "企业账号" ,信息可以随意填写 ...

  5. java对接微信支付收不到支付通知问题(亲身实践)

    问题描述: 用java对接微信支付时,统一下单接口正常.但是用户扫码付款成功后,设置用于回调的notify_url对应的接口并没有收到请求(这个url测试过,是正常的且外网能访问的). 由于官方文档没 ...

  6. java对接支付宝支付

    java对接支付宝支付演示 现在有不少的项目都需要对接支付,这里主要是进行讲解对接支付宝H5支付 废话不多说 上代码 引入支付宝官方的sdk <!-- https://mvnrepository ...

  7. Java开发在线支付平台视频教程(AVI格式)

    Java开发在线支付平台视频教程,主要教授如何让自己的网站与银行系统进行对接.如何让用户通过网上银行向你支付费用等内容,全AVI视频格式 JAVA开发视频内容目录: Java开发在线支付平台视频教程_ ...

  8. [转载]用Java开发企业级无线应用

    用Java开发企业级无线应用 Java技术正日益影响着我们的生活,从桌面到Web应用,到服务器端组件,再到智能移动终端(手机.PDA),Java技术无处不在.利用Java技术,结合J2ME和J2EE平 ...

  9. java版+支付宝支付和微信支付(一)

    最近公司在做支付模块,在接入过程中遇到了很多坑,费了不少事,现在分享一下接入方法,也记录一下,以后可能还用的到.用的是支付宝的即时到帐支付功能和微信的扫码支付功能,相比起来,个人感觉支付宝的文档和接入 ...

  10. 微信小程序-JAVA实现微信支付功能(微信支付2.0)

    微信小程序-JAVA实现微信支付功能(微信支付2.0) 一.前言 本博客主要介绍JAVA后台与微信小程序(UNI-APP或者原生微信小程序)的微信支付的实现,如果是APP或者H5的开发暂时不支持,具体 ...

最新文章

  1. .net new一个类为什么报空指针_谈谈.NET对象生命周期
  2. Linux系统文件目录
  3. 关于最长公共子序列的执行过程
  4. Request_获取请求头数据
  5. a标签跳转后关闭当前页面_微信小程序2020-day-2 导航项目(跳转三种形态)
  6. SP1693 COCONUTS - 题解
  7. vue 保存时清空iuput_vue清空input file
  8. C/C++蓝桥杯1 备赛准备
  9. easycode不推荐使用_为什么?mysql不推荐使用uuid或者雪花id作为主键?
  10. HDU 6351 (Beautiful Now) 2018 Multi-University Training Contest 5
  11. 怎么学计算机制作ppt,怎样制作ppt详细步骤(电脑怎么做ppt新手)
  12. k3金蝶 java版本_金蝶KIS旗舰版和K3wise的不同
  13. Hive函数详解(中文)
  14. 中级php工程师笔试,PHP工程师笔试题目及行测题型示例
  15. php unlink没有权限,php中删除文件用unlink函数权限判断_PHP教程
  16. Linux查询网络配置相关命令
  17. 【热更新】游戏热更新方案
  18. Python语言程序设计第七章 - 组合数据类型 - 6.1
  19. kubenates(k8s)部署全家桶
  20. html产品720度旋转,720度全视角全景相机

热门文章

  1. 使用串口连接Arduino与树莓派开发板
  2. 免费P2P穿透通信(4) RDT可靠通信模块测试使用
  3. 计算机网络-自顶向下笔记-可靠数据传输原理(三种rdt)
  4. Mac 系统 Arduino IDE 找不到开发板端口的解决方法
  5. 拼图 html5,HTML5 拼图游戏
  6. 常用类/ID命名举例
  7. mysql dump 1449_关于mysqldump的ERROR 1449 问题
  8. Learn to say “fuck you” to the world every once in a while
  9. 计算机中专生未来三年的规划,职业中专三年发展规划.doc
  10. DRAM基本单元最为通俗易懂的图文解说