注意事项
在做微信小程序支付之前首先要认证小程序,其次申请微信支付功能。申请微信功能成功后,会有开发文档,在里面一定要熟读开发文档,否则会有坑。
微信小程序支付顺序 -> 后端调统一下单(API) -> 小程序调用 wx.requestPayment()-> 后端回调处理。
一、开发准备
下载微信SDK点这里
修改 WXPay.java 文件,我们所有的加密方式最好统一是MD5
二、配置appid,mch_id,mch_key
appid 为小程序的appid
mch_id 为微信商户平台的id
mch_key 为微信商户 API密钥
写一个类继承 WXPayConfig 然后实现他的方法,WXPayConfig 的所有抽象方法记得改成 public。
完整代码如下:
package com.hyq.hyqcloud.service.user.impl;
import com.hyq.hyqcloud.pay.IWXPayDomain;
import com.hyq.hyqcloud.pay.WXPayConfig;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
/**
* @Author big uncle
* @Date 2020/3/26 16:00
* @module HYQ_APP
**/
@Component
public class PayConfig extends WXPayConfig {
/**
* 你的appid"
**/
@Value("${hyq.tripartite.wechat.applets.appId}")
private String appId;
/**
* 你的商户id
**/
@Value("${hyq.tripartite.wechat.applets.mch_id}")
private String mchID;
/**
* 你的商户api安全key,在商户平台api安全目录下可设置
**/
@Value("${hyq.tripartite.wechat.applets.mch_key}")
private String key;
private byte[] certData;
public PayConfig() throws Exception {
// String certPath = "classpath: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();
}
/**
* 获取 App ID
* @return App ID
*/
@Override
public String getAppID(){
return appId;
}
/**
* 获取 Mch ID
*
* @return Mch ID
*/
@Override
public String getMchID(){
return mchID;
}
/**
* 获取 API 密钥
* @return API密钥
*/
@Override
public String getKey(){
return key;
}
/**
* 获取商户证书内容
* @return 商户证书内容
*/
@Override
public InputStream getCertStream(){
ByteArrayInputStream certBis = new ByteArrayInputStream(this.certData);
return certBis;
}
/**
* 获取WXPayDomain, 用于多域名容灾自动切换
* @return
*/
@Override
public IWXPayDomain getWXPayDomain() {
return new IWXPayDomain() {
@Override
public void report(String domain, long elapsedTimeMillis, Exception ex) {
}
@Override
public DomainInfo getDomain(WXPayConfig config) {
return new DomainInfo("api.mch.weixin.qq.com", false);
}
};
}
}
支付代码如下:
@Autowired
private PayConfig payConfig;
@Override
public ResponseData pay(RequestData<String, AppletsUserVo> requestData) {
// 请求数据
AppletsUserVo appletsUserVo = requestData.getUser();
// 请求数据库
AppletsUser appletsUser = appletsUserMapper.selectById(appletsUserVo.getId());
try {
return ResponseData.successResponse(toPay("旅游度假村","1",appletsUser.getOpenId()));
}catch(Exception e){
e.printStackTrace();
return ResponseData.failureResponse(1,"支付失败");
}
}
/**
* projectDesc 商品描述 也可是商品名称
* price 价格
* openId 这个我就不说了
**/
private Map<String,String> toPay(String projectDesc,String price,String openId) throws Exception {
Map<String,String> resultMap = new HashMap<>(15);
// 得到套餐信息
WXPay wxpay = new WXPay(payConfig);
//生成的随机字符串
String nonce_str = WXPayUtil.generateNonceStr();
//获取本机的ip地址
InetAddress addr = null;
try {
addr = InetAddress.getLocalHost();
} catch (UnknownHostException e) {
e.printStackTrace();
}
String spbill_create_ip = addr.getHostAddress();
String out_trade_no= WXPayUtil.generateNonceStr();
//统一下单接口参数
HashMap<String, String> data = new HashMap<String, String>(20);
// 商品描述
data.put("body", projectDesc);
data.put("device_info", "WEB");
// 商户订单号
data.put("out_trade_no",out_trade_no);
// 价格
data.put("total_fee", price);
// 终端ip
data.put("spbill_create_ip", spbill_create_ip);
// 回调地址
data.put("notify_url", "https://nuclean.cn/api/guardClient/appletsUser/payCallBack");
// 交易类型 小程序 app h5 不同的平台值不一样
data.put("trade_type","JSAPI");
data.put("openid", openId);
Map<String, String> rMap = wxpay.unifiedOrder(data);
System.out.println("统一下单接口返回: " + rMap);
String return_code = (String) rMap.get("return_code");
String result_code = (String) rMap.get("result_code");
String nonceStr = WXPayUtil.generateNonceStr();
resultMap.put("nonceStr", nonceStr);
Long timeStamp = System.currentTimeMillis() / 1000;
if ("SUCCESS".equals(return_code) && return_code.equals(result_code)) {
String prepayid = rMap.get("prepay_id");
resultMap.put("package", "prepay_id="+prepayid);
resultMap.put("signType", "MD5");
//这边要将返回的时间戳转化成字符串,不然小程序端调用wx.requestPayment方法会报签名错误
resultMap.put("timeStamp", timeStamp + "");
//再次签名,这个签名用于小程序端调用wx.requesetPayment方法
resultMap.put("appId",payConfig.getAppID());
resultMap.put("paySign", WXPayUtil.generateSignature(resultMap, payConfig.getKey()));
}
return resultMap;
}
回调代码如下:
@RequestMapping("/payCallBack")
public ResponseData payCallBack(HttpServletRequest request, HttpServletResponse response) throws Exception {
String str = IoUtil.read(request.getInputStream(),"utf-8");
log.debug(str);
Map<String,String> map = WXPayUtil.xmlToMap(str);
String state = map.get("return_code");
String resultSign = map.get("sign");
// 制空
if(!"SUCCESS".equals(state)){
String sign = WXPayUtil.generateSignature(map,"商户api密钥");
if(sign.equals(resultSign)){
// redis 上锁 并拒绝重复请求 加失效时间
// 通过id去数据库查询
// 比对 appid 商户id
// 比对 openid 确定同一个人 比对 金额
}
}
log.debug(JSONObject.toJSONString(map));
return ResponseData.successResponse();
}
log.debug 方法是 lombok的
IoUtil 是 hutool的 大家可以百度学习
小程序代码就很简单了,后端支付传回来的数据和 wx.requestPayment()参数一一对应就行。
网友评论