微信小程序支付功能,完成整个交易的思路和代码
开发工具:微信开发者工具,Intellij idea 2018
框架:spring boot
交易流程图:
下面直接上代码:
1.在wxml添加一个支付按钮,点击监听payMethod方法
<view><button bindtap='payMethod'>支付</button></view>
2.payMethod方法种调用wx.login微信接口,获取code。调用getOpenId方法
/**
* 支付例子
* const config = require('../../config')获取全局配置
* const appInstance = getApp();获取全局上下文实例
*/
payMethod(){
let _this = this ;//当前上下文
//获取登录code
wx.login({
success: result =>{
console.info(result.code)
//获取openid
_this.getOpenId(result.code)
}
});
},
3.将code作为参数,请求Java服务,获取openId
/**
* 用code作为参数请求Java服务,像微信服务换取openId,access_token
* 此处用到openId,access_token暂时无用,为安全起见,此字段不返回客户端
*/
getOpenId(code){
let _this = this;//当前上下文
wx.request({
url: config.localServer + 'api/wc/jscode2session.wc',
data: { code: code},
method: 'POST',
success: result =>{
console.info('返回openId')
console.info(result.data)
_this.generateOrder(result.data.data.openid)
},
fail:() => {
console.info('fail')
},
complete: () => {
// complete
}
})
},
4.Java服务jscode2session接口,调用微信sns/jscode2session服务,获取openId,将openid返回给小程序客户端
/**
* 换取 用户唯一标识 OpenID 和 会话** session_key ->>> oopenid 和 session_key 组成会话标识,发给客户端,客户端凭借这个标识进行通信
* 会话** session_key 是对用户数据进行 加密签名 的**。
* 为了应用自身的数据安全,开发者服务器不应该把会话**下发到小程序,也不应该对外提供这个**。
* 临时登录凭证 code 只能使用一次
* @param request
* @param requestEntity
* @param code
* @return
*/
@PostMapping("/jscode2session.wc")
public ResponseBean jscode2session(HttpServletRequest request, HttpEntity<String> requestEntity,String code) {
HttpMethod requestMethod = HttpMethod.resolve(request.getMethod());
String body = requestEntity.getBody();
Map<String,String> mapParam = JacksonUtil.fromJson(body,Map.class);
code = mapParam.get("code");
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
requestEntity = new HttpEntity<>(requestEntity.getBody(), headers);
ResponseEntity<String> response = null;
response = restTemplate.exchange(
wcHost+"sns/jscode2session?appid=wxd0ad6e09b4f09a2a&secret=919f06b91b6adbe682c0bd9edc0a008f&js_code="+code+"&grant_type=authorization_code",
requestMethod,
requestEntity,
String.class);
String result = response.getBody();
LOGGER.info("result="+result);
Map<String,String> mapResult = JacksonUtil.fromJson(result,Map.class);
return ResponseBean.response(mapResult);
}
5.小程序获取openid,调用generateOrder方法,生成订单信息,调用Java服务payOrderPublic.wc接口,以获取支付参数
/**
* 用openid,在Java服务请求微信服务,获取支付的请求参数
*/
generateOrder(openid){
var _this = this
var paymentPo={
openid: openid,
total_fee: '0.1',
mch_id: 'mch_id',
body: '支付测试',
detail: 'detail',
attach: '假酒'
}
wx.request({
url: config.localServer + 'api/wc/payOrderPublic.wc',
method: 'POST',
data: paymentPo,
success: result => {
console.info(result)
var pay = result.data.data
//发起支付
var timeStamp = pay[0].timeStamp;
console.info("timeStamp:" + timeStamp)
var packages = pay[0].package;
console.info("package:" + packages)
var paySign = pay[0].paySign;
console.info("paySign:" + paySign)
var nonceStr = pay[0].nonceStr;
console.info("nonceStr:" + nonceStr)
var param = { "timeStamp": timeStamp, "package": packages, "paySign": paySign, "signType": "MD5", "nonceStr": nonceStr };
_this.pay(param)
},
})
},
6.Java服务payOrderPublic.wc接口,将参数生成签名,打包成xml格式,请求微信的统一下单接口/pay/unifiedorder,将获取的结果进行再次签名,得到支付接口需要的参数,返回给小程序客户端。
/**
* 商户server调用支付统一下单
// * @param openid 用户唯一标识
// * @param mch_id 商品编号
// * @param total_fee 商品价格
// * @param body 商品描述
// * @param detail 商品详情
// * @param attach 附加数据
// * @param time_start 交易开始时间
// * @param time_expire 交易结束时间
* @return
*/
@PostMapping("/payOrderPublic.wc")
public ResponseBean payOrderPublic(HttpServletRequest request,HttpEntity<String> requestEntity,PaymentPo paymentPo) throws UnsupportedEncodingException, DocumentException {
paymentPo = JacksonUtil.fromJson(requestEntity.getBody(),PaymentPo.class);
paymentPo.setBody(new String(paymentPo.getBody().getBytes("UTF-8"),"ISO-8859-1")) ;
// String appid = "替换为自己的小程序ID";//小程序ID
//当前时间
String today = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
//生成8位随机数
String code = PayUtil.createCode(8);
//商户订单号
String out_trade_no = paymentPo.getMch_id()+today+code;//商户订单号
String spbill_create_ip = HttpUtil.getIpAddress(request);//终端IP
String notify_url = "http://www.weixin.qq.com/wxpay/pay.php";//通知地址
String trade_type = "JSAPI";//交易类型
paymentPo.setAppid(appid);
//随机字符串 32位
String str=UUIDHexGeneratorUtil.generate();
paymentPo.setNonce_str(str);
paymentPo.setOut_trade_no(out_trade_no);
paymentPo.setSpbill_create_ip(spbill_create_ip);
paymentPo.setNotify_url(notify_url);
paymentPo.setTrade_type(trade_type);
// 把请求参数放进hashmap
Map<String,String> sParaTemp = new HashMap<String,String>();
sParaTemp.put("appid", paymentPo.getAppid());
sParaTemp.put("mch_id", paymentPo.getMch_id());
sParaTemp.put("nonce_str", paymentPo.getNonce_str());
sParaTemp.put("body", paymentPo.getBody());
sParaTemp.put("out_trade_no", paymentPo.getOut_trade_no());
sParaTemp.put("total_fee",paymentPo.getTotal_fee());
sParaTemp.put("spbill_create_ip", paymentPo.getSpbill_create_ip());
sParaTemp.put("notify_url",paymentPo.getNotify_url());
sParaTemp.put("trade_type", paymentPo.getTrade_type());
sParaTemp.put("openid", paymentPo.getOpenid());
// 除去map中的空值和签名参数
Map<String,String> sPara = PayUtil.paraFilter(sParaTemp);
// 把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串
String prestr = PayUtil.createLinkString(sPara);
String key = "&key=替换为商户支付**"; // 商户支付**
//MD5运算生成签名
String mysign = PayUtil.sign(prestr, key, "utf-8").toUpperCase();
paymentPo.setSign(mysign);
//打包要发送的xml
String respXml = MessageUtil.messageToXML(paymentPo);
// 打印respXml发现,得到的xml中有“__”不对,应该替换成“_”
respXml = respXml.replace("__", "_");
String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";//统一下单API接口链接
String param = respXml;
String result =PayUtil.httpRequest(url, "POST", param);
// 将解析结果存储在HashMap中
Map<String,String> map = new HashMap<String,String>();
InputStream in = new ByteArrayInputStream(result.getBytes());
// 读取输入流
SAXReader reader = new SAXReader();
Document document = reader.read(in);
// 得到xml根元素
Element root = document.getRootElement();
// 得到根元素的所有子节点
@SuppressWarnings("unchecked")
List<Element> elementList = root.elements();
for (Element element : elementList) {
map.put(element.getName(), element.getText());
}
// 返回信息
String return_code = map.get("return_code");//返回状态码
String return_msg = map.get("return_msg");//返回信息
System.out.println("return_msg"+return_msg);
Map<String,Object> resultMap = new HashMap<String,Object>();
if(return_code=="SUCCESS"||return_code.equals(return_code)){
// 业务结果
String prepay_id = map.get("prepay_id");//返回的预付单信息
String nonceStr=UUIDHexGeneratorUtil.generate();
resultMap.put("nonceStr", nonceStr);
resultMap.put("package", "prepay_id="+prepay_id);
Long timeStamp= System.currentTimeMillis()/1000;
resultMap.put("timeStamp", timeStamp+"");
String stringSignTemp = "appId="+appid+"&nonceStr=" + nonceStr + "&package=prepay_id=" + prepay_id+ "&signType=MD5&timeStamp=" + timeStamp;
//再次签名
String paySign=PayUtil.sign(stringSignTemp, "&key=替换为自己的**", "utf-8").toUpperCase();
resultMap.put("paySign", paySign);
}
return ResponseBean.response(resultMap) ;
}
7.小程序端得到支付参数,用pay方法调用微信的支付接口,完成支付
/**
* 支付的动作
*/
pay(param){
console.info("支付")
console.info(param)
wx.requestPayment({
timeStamp: param.timeStamp,
nonceStr: param.nonceStr,
package: param.package,
signType: param.signType,
paySign: param.paySign,
success: (result) => {
console.info("支付")
console.info(result)
},
fail: (result) => {
console.info("支付失败")
console.info(result)
},
complete: () => {
console.info("pay complete")
}
})
}