uni-app实现微信APP支付的全过程详解(附赠全部代码)

背景

最近项目使用uni-app实现微信支付,把过程简单记录下,帮助那些刚刚基础uni-app,苦于文档的同学们。
整体来说实现过程和非uni-app的实现方式没有太大不同,难点就在于uni-app对于orderInfo的格式没有说明。

准备工作

  1. 申请了商户号,拿到了API秘钥。这个需要微信开发平台,相关的工作大家百度。
  2. 后面代码里用到的appid和秘钥之类需要实现申请号。
  3. 在uni-app manifest.json 配置sdk支付权限
    uni-app实现微信APP支付的全过程详解(附赠全部代码)_第1张图片

前端代码

  1. onload阶段获取了可用支付列表,这里我们只用到了微信支付。
  2. requestPayment
    a. getOrderInfo 获取到订单信息,主要是prepayid,对应统一下单api的返回值。
    b. uni.requestPayment发起支付,效果就是弹出微信支付框输入密码支付。第一个参数是“wxpay”,第二个参数就是OrderInfo.

前端代码很简单,重点是如何让后端返回OrderInfo以及OrderInfo的格式。






后端代码(springboot)

核心代码

import com.alibaba.fastjson.JSONObject;
import com.bian.common.core.domain.AjaxResult;
import com.bian.common.utils.StringUtils;
import com.bian.framework.config.jwt.AuthService;
import com.bian.sales.entity.Constant;
import com.bian.sales.entity.PayInfo;
import com.bian.sales.service.IWxService;
import com.bian.sales.util.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import com.thoughtworks.xstream.XStream;
import org.springframework.http.HttpEntity;
import org.slf4j.Logger;
import javax.servlet.http.HttpServletRequest;
import java.util.*;

@Service
public class WxServiceImpl implements IWxService
{
    Logger logger = LoggerFactory.getLogger(WxServiceImpl.class);

    @Override
    public AjaxResult goWeChatPay(String brokerId, HttpServletRequest request) throws Exception {
        String clientIP = CommonUtil.getClientIp(request);
        logger.error("openId: " + brokerId + ", clientIP: " + clientIP);
        String randomNonceStr = RandomUtils.generateMixString(32);
        Map result = unifiedOrder(brokerId, clientIP, randomNonceStr);
        System.out.println(request.toString());
        if(StringUtils.isBlank(result.get("prepay_id"))) {
           return AjaxResult.error("支付错误");
        } else {
            logger.info("支付成功");
            Map jsonObject = new LinkedHashMap();
            String noncestr = RandomUtils.generateMixString(32);
            String prepayid = result.get("prepay_id");
            String timestamp = String.valueOf(new Date().getTime()/1000);
            jsonObject.put("appid",Constant.APP_ID);
            jsonObject.put("noncestr",noncestr);
            jsonObject.put("package","Sign=WXPay");
            jsonObject.put("partnerid",Constant.MCH_ID);
            jsonObject.put("prepayid",result.get("prepay_id"));
            jsonObject.put("timestamp",new Date().getTime()/1000);
            jsonObject.put("sign",getSignforPayment(noncestr,prepayid,timestamp ));
            return AjaxResult.success(jsonObject);
        }
    }

    /**
     * @Function: 去支付
     * @author:   YangXueFeng
     * @Date:     2019/6/14 16:50
     */
    /**
     * 调用统一下单接口
     * @param brokerId
     */
    private Map  
    (String brokerId, String clientIP, String randomNonceStr) {

        try {

            //生成预支付交易单,返回正确的预支付交易会话标识后再在APP里面调起支付
            String url = Constant.URL_UNIFIED_ORDER;

            PayInfo payInfo = createPayInfo(brokerId, clientIP, randomNonceStr);
            String md5 = getSign(payInfo);
            payInfo.setSign(md5);

            logger.error("md5 value: " + md5);

            String xml = CommonUtil.payInfoToXML(payInfo);
            xml = xml.replace("__", "_").replace("", "1");
            //xml = xml.replace("__", "_").replace("", "");
            logger.error(xml);

            StringBuffer buffer = HttpUtil.httpsRequest(url, "POST", xml);
            logger.error("unifiedOrder request return body: \n" + buffer.toString());
            Map result = CommonUtil.parseXml(buffer.toString());


            String return_code = result.get("return_code");
            if(StringUtils.isNotBlank(return_code) && return_code.equals("SUCCESS")) {

                String return_msg = result.get("return_msg");
                if(StringUtils.isNotBlank(return_msg) && !return_msg.equals("OK")) {
                    logger.error("统一下单错误!");
                    return null;
                }

                String prepay_Id = result.get("prepay_id");
                return result;

            } else {
                return null;
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     * 生成统一下单接口的请求参数
     * https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1
     * @param brokerId
     * @param clientIP
     * @param randomNonceStr
     * @return
     */
    private PayInfo createPayInfo(String brokerId, String clientIP, String randomNonceStr) {
        clientIP ="222.72.148.34";
        Date date = new Date();
        String timeStart = TimeUtils.getFormatTime(date, Constant.TIME_FORMAT);
        String timeExpire = TimeUtils.getFormatTime(TimeUtils.addDay(date, Constant.TIME_EXPIRE), Constant.TIME_FORMAT);

        String randomOrderId = CommonUtil.getRandomOrderId(); //生成随机商品订单号

        PayInfo payInfo = new PayInfo();
        payInfo.setAppid(Constant.APP_ID);
        payInfo.setMch_id(Constant.MCH_ID);
        payInfo.setDevice_info("WEB");
        payInfo.setNonce_str(randomNonceStr);
        payInfo.setSign_type("MD5");  //默认即为MD5
        payInfo.setBody("必安glJSAPI支付测试");
        payInfo.setAttach("支付测试4luluteam");
        payInfo.setOut_trade_no(randomOrderId);
        payInfo.setTotal_fee(1);
        payInfo.setSpbill_create_ip(clientIP);
        payInfo.setTime_start(timeStart);
        payInfo.setTime_expire(timeExpire);
        payInfo.setNotify_url(Constant.URL_NOTIFY);
        payInfo.setTrade_type("APP");
        payInfo.setLimit_pay("no_credit");
//        payInfo.setOpenid(brokerId);
        return payInfo;
    }

    private String getSign(PayInfo payInfo) throws Exception {
        StringBuffer sb = new StringBuffer();
        sb.append("appid=" + payInfo.getAppid())
                .append("&attach=" + payInfo.getAttach())
                .append("&body=" + payInfo.getBody())
                .append("&device_info=" + payInfo.getDevice_info())
                .append("&limit_pay=" + payInfo.getLimit_pay())
                .append("&mch_id=" + payInfo.getMch_id())
                .append("&nonce_str=" + payInfo.getNonce_str())
                .append("¬ify_url=" + payInfo.getNotify_url())
//                .append("&openid=" + payInfo.getOpenid())
                .append("&out_trade_no=" + payInfo.getOut_trade_no())
                .append("&sign_type=" + payInfo.getSign_type())
                .append("&spbill_create_ip=" + payInfo.getSpbill_create_ip())
                .append("&time_expire=" + payInfo.getTime_expire())
                .append("&time_start=" + payInfo.getTime_start())
                .append("&total_fee=" + payInfo.getTotal_fee())
                .append("&trade_type=" + payInfo.getTrade_type())
                .append("&key=" + Constant.API_KEY);

        System.out.println("排序后的拼接参数:" + sb.toString());
        return CommonUtil.getMD5(sb.toString().trim()).toUpperCase();
    }

    private String getSignforPayment(String noncestr,String prepayid,String timestamp) throws Exception {
        StringBuffer sb = new StringBuffer();
        sb.append("appid=" +Constant.APP_ID)
                .append("&noncestr=" + noncestr)
                .append("&package=" + "Sign=WXPay")
                .append("&partnerid=" + Constant.MCH_ID)
                .append("&prepayid=" + prepayid)
                .append("×tamp=" + timestamp)
                .append("&key=" + Constant.API_KEY);

        System.out.println("排序后的拼接参数:" + sb.toString());
        return CommonUtil.getMD5(sb.toString().trim()).toUpperCase();
    }

}

代码说明
以上代码goWeChatPay从controller层跳转并返回结果给controller接口。所有过程参考微信官方文档的2个接口

  1. 统一下单接口后端
  2. 调起支付接口前端已实现

unifiedOrder对应了统一下单接口,看起来很复杂,其实就做了一件事就是拼接参数。拼接参数时涉及到了签名算法,理解这个算法是关键,建议花时间理解透彻这个算法。

createPayInfo,创建一个PayInfo的类,对应了提交的各个参数。
getSign,签名算法的具体实现,获得提交参数的sign。

以上完成了统一下单接口的过程,如果return_code返回“SUCCESS”,result_code返回OK,我们会获得prepay_id(预支付交易会话标识),到这里已经完成了后端内容。但为了使用uni-app我们需要按照如下格式返回给前端,

{"data": {
		"appid": "wx0411fa6a39d61297",
		"noncestr": "5JigiIJicbq8hQI2",
		"package": "Sign=WXPay",
		"partnerid": "1230636401",
		"prepayid": "wx21204902147233e222e12d451613768000",
		"timestamp": 1571662142,
		"sign": "0E5C9B9B1C8D7497A234CCC3C721AB1F"
	},
	"statusCode": 200,
	"header": {
		"Content-Type": "text/plain;charset=UTF-8",
		"X-Android-Response-Source": "NETWORK 200",
		"Date": "Mon, 21 Oct 2019 12:49:02 GMT",
		"EagleId": "74cf71a315716621419605339e",
		"Vary": "[Accept-Encoding, Accept-Encoding]",
		"X-Android-Received-Millis": "1571662145735",
		"Timing-Allow-Origin": "*",
		"_": "HTTP/1.1 200 OK",
		"X-Android-Selected-Protocol": "http/1.1",
		"Connection": "keep-alive",
		"Via": "cache28.l2et15-1[208,0], kunlun5.cn1241[504,0]",
		"X-Android-Sent-Millis": "1571662145071",
		"Access-Control-Allow-Origin": "*",
		"Server": "Tengine",
		"Transfer-Encoding": "chunked"
	},
	"errMsg": "request:ok"
}

重点是data部分,就是uni-app要求的OrderInfo的格式,getSignforPayment就是该部分的签名算法。

以上如果实行正确,应该就可以正常发起微信支付。

参考文档

https://uniapp.dcloud.io/api/plugins/payment?id=requestpayment

你可能感兴趣的:(uni-app)