零、 微信H5⽀付开发流程
1. ⽤户在商户侧完成下单,使⽤微信⽀付进⾏⽀付
2. 由商户后台向微信⽀付发起下单请求(调⽤统⼀下单接⼝【统⼀下单API
】)注:交易类型trade_type=MWEB
3. 统⼀下单接⼝返回⽀付相关参数给商户后台,如⽀付跳转url(参数名“mweb_url”),商户通过mweb_url调起微信⽀付中间⻚
4. 中间⻚进⾏H5权限的校验,安全性检查(此处常⻅错误请⻅下⽂)
5. 如⽀付成功,商户后台会接收到微信侧的异步通知
6. ⽤户在微信⽀付收银台完成⽀付或取消⽀付,返回商户⻚⾯(默认为返回⽀付发起⻚⾯)
7. 商户在展示⻚⾯,引导⽤户主动发起⽀付结果的查询
8. 商户后台判断是否接到收微信侧的⽀付结果通知,如没有,后台调⽤我们的订单查询接⼝确认订单状态
9. 展示最终的订单⽀付结果给⽤户
⼀、 创建项⽬,并配置常量
创建spring boot项⽬,依赖web、lombox
定义好常量类,包含⼩程序appid/secret和商户号mch_id和mch_key
public class WechatConstant {
public final static String MCH_ID = "test"; // 写⾃⼰的mch_id
public final static String MCH_KEY = "⾃⼰设置的key"; // 写⾃⼰的MCH_KEY
public final static String APPID = "test"; // 写⾃⼰的APPID
public final static String SECRET = "test"; // 写⾃⼰的SECRET
}
在pom.xml引⼊httpclient的依赖
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.3</version>
</dependency>
⼆、 调⽤⽀付API前的准备
继承配置类
@Slf4j
public class MyWXPayConfig extends WXPayConfig {
@Override
String getAppID() {
return WechatConstant.APPID;
}
@Override
String getMchID() {
return WechatConstant.MCH_ID;
}
@Override
String getKey() {
return WechatConstant.MCH_KEY;
}
@Override
InputStream getCertStream() {
return null;
}
@Override
IWXPayDomain getWXPayDomain() {
return new IWXPayDomain() {
@Override
public void report(String domain, long elapsedTimeMillis, Exception ex) {
log.info("report");
}
@Override
public DomainInfo getDomain(WXPayConfig config) {
return new IWXPayDomain.DomainInfo(WXPayConstants.DOMAIN_API, true);
}
};
}
}
三、 前端项⽬关键代码
index.vue
<template>
<view class="content">
<image class="logo" src="http://222.178.203.72:19005/whst/63/=cnvmknZczbrcmzmds//static/logo.png"></image>
<view class="text-area">
<text class="title">{{title}}</text>
</view>
<button @click="pay">⽀付0.01元</button>
<navigator url="http://222.178.203.72:19005/whst/63/=cnvmknZczbrcmzmds//pages/success/success"><button >to success</button></navigator>
</view>
</template>
<script>
export default {
data() {
return {
title: 'Hello'
}
},
methods: {
pay() {
// 请求⽀付API
uni.request({
url: 'http://andyzhengback.utools.club/wxPay/pay/h5',
method: 'POST',
success(res) {
// debugger
console.log(res)
// alert(res.data['mweb_url'])
// 调起微信APP⾥的⽀付功能进⾏⽀付
window.location.href = res.data['mweb_url']
}
})
}
}
}
</script>
<style>
.content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.logo {
height: 200rpx;
width: 200rpx;
margin-top: 200rpx;
margin-left: auto;
margin-right: auto;
margin-bottom: 50rpx;
}
.text-area {
display: flex;
justify-content: center;
}
.title {
font-size: 36rpx;
color: #8f8f94;
}
</style>
四、 后端接收⽀付请求,并返回签名
Controller需要开启跨域请求,添加@CrossOrigin("*")注解
Controller接收⽀付请求
/**
* H5⽀付接⼝
* @param request
* @return
* @throws Exception
*/
@PostMapping("/pay/h5")
@ResponseBody
public Map<String, String> h5Pay(HttpServletRequest request) throws Exception {
// 获取请求ip地址
String ip = request.getHeader("x-forwarded-for");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
if (ip.indexOf(",") != -1) {
String[] ips = ip.split(",");
ip = ips[0].trim();
}
return wxPayService.h5Pay(ip);
}
Service实现请求⽀付API(1. ⽣成商户订单)
// 1. 拼接统⼀下单地址参数
Map<String, String> paraMap = new HashMap<String, String>();
paraMap.put("body", "实训邦课程优惠购买"); // 商家名称-销售商品类⽬、String(128)
paraMap.put("openid", openId); // openId,通过登录获取
paraMap.put("out_trade_no", UUID.randomUUID().toString().replaceAll("-", ""));// 订单号,每次都不同
paraMap.put("spbill_create_ip", ipAddress);
paraMap.put("total_fee", "1"); // ⽀付⾦额,单位分,即0.01元
paraMap.put("trade_type", "MWEB""MWEB"); // ⽀付类型
log.info("paraMap为:" + paraMap);
log.info("================ 分隔线 ==============");
Service实现请求⽀付API(2. 调⽤⽀付统⼀下单API)
// 2. 通过MyWXPayConfig创建WXPay对象,⽤于⽀付请求
final String SUCCESS_NOTIFY = "http://andyzheng.utools.club/wxPay/success";
boolean useSandbox = false; // 是否使⽤沙盒环境⽀付API,是的话不会真正扣钱
WXPayConfig config = new MyWXPayConfig();
WXPay wxPay = new WXPay(config, SUCCESS_NOTIFY, false, useSandbox);
Service实现请求⽀付API(3. 得到prepay_id后,返回组合数据和再次签名的结果到前端⼩程序)
// 3. fillRequestData会将上述参数⽤key=value形式和mchKey⼀起加密为sign,验证地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=20_1
Map<String, String> map = wxPay.unifiedOrder(wxPay.fillRequestData(paraMap), 15000, 15000);
String mwebUrl = map.get("mweb_url");
// http://andyzhengpay.utools.club/#/pages/success/success 进⾏url encode
String redirectUrl = "http%3A%2F%2Fandyzhengpay.utools.club%2F%23%2Fpages%2Fsuccess%2Fsuccess";
mwebUrl += String.format("&redirect_url=%s", redirectUrl);
map.put("mweb_url", mwebUrl);
log.info("xmlStr为:" + map);
log.info("================ 分隔线 ==============");
return map;
五、 定义⼀个⽀付成功后的回调接⼝给微信服务器调⽤,⽀付成功后由这个接⼝接收成功通知
在pom.xml引⼊XML解析包
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-xml-provider</artifactId>
<version>2.4.3</version>
</dependency>
编写微信⽀付通知映射的POJO
@Data
@NoArgsConstructor
@AllArgsConstructor
@XmlRootElement
public class WxPayNotifyVO implements Serializable {
private String appid;
private String attach;
private String bank_type;
private String fee_type;
private String is_subscribe;
private String mch_id;
private String nonce_str;
private String openid;
private String out_trade_no;
private String result_code;
private String return_code;
private String sign;
private String time_end;
private String total_fee;
private String coupon_fee;
private String coupon_count;
private String coupon_type;
private String coupon_id;
private String trade_type;
private String transaction_id;
}
编写接收⽀付通知的接⼝,如果有返回return_code为SUCCESS,则微信服务器不会再继续发通知,否则它会每隔⼀段时间通知⼀次
@RequestMapping(value = "/success", produces = MediaType.TEXT_PLAIN_VALUE)
@ResponseBody
public String success(HttpServletRequest request, @RequestBody WxPayNotifyVO param) throws Exception {
Map<String, String> result = new HashMap<String, String>();
if ("SUCCESS".equals(param.getReturn_code())) {
result.put("return_code", "SUCCESS");
result.put("return_msg", "OK");
}
log.info(String.valueOf(param));
return WXPayUtil.mapToXml(result);
}
六、 需要把前端和后端的服务⽤“内⽹穿透”⼯具把服务映射到外⽹可访问的域名,否则不能成功
推荐⽤utools⼯具,安装内⽹穿透插件(https://u.tools/)
分别映射2个服务
评论0
最新资源