zoukankan      html  css  js  c++  java
  • uni-app开发经验分享八: 实现微信APP支付的全过程详解

    背景

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

    准备工作

    1. 申请了商户号,拿到了API秘钥。这个需要微信开发平台,相关的工作大家百度。
    2. 后面代码里用到的appid和秘钥之类需要实现申请号。
    3. 在uni-app manifest.json 配置sdk支付权限

    前端代码

    1. onload阶段获取了可用支付列表,这里我们只用到了微信支付。
    2. requestPayment  

              a. getOrderInfo 获取到订单信息,主要是prepayid,对应统一下单api的返回值。

              b. uni.requestPayment发起支付,效果就是弹出微信支付框输入密码支付。第一个参数是“wxpay”,第二个参数就是OrderInfo.

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

      

    前端代码如下:

    <template>
        <view>
            <page-head :title="title"></page-head>
            <view class="uni-padding-wrap">
                <view style="background:#FFF; padding:50upx 0;">
                    <view class="uni-hello-text uni-center">支付金额</text></view>
                    <view class="uni-h1 uni-center uni-common-mt">
                        <text class="rmbLogo">¥</text>
                        <input class="price" type="digit" :value="price" maxlength="4" @input="priceChange" />
                    </view>
                </view>
                <view class="uni-btn-v uni-common-mt">
                    <!-- #ifdef APP-PLUS -->
                    <template v-if="providerList.length > 0">
                        <button v-for="(item,index) in providerList" :key="index" @click="requestPayment(item,index)"
                            :loading="item.loading">{{item.name}}支付</button>
                    </template>
                    <!-- #endif -->
                </view>
            </view>
        </view>
        </view>
    </template>
    <script>
        export default {
            data() {
                return {
                    title: 'request-payment',
                    loading: false,
                    price: 1,
                    providerList: []
                }
            },
            onLoad: function() {
                // #ifdef APP-PLUS
                uni.getProvider({
                    service: "payment",
                    success: (e) => {
                        console.log("payment success:" + JSON.stringify(e));
                        let providerList = [];
                        e.provider.map((value) => {
                            switch (value) {
                                case 'alipay':
                                    providerList.push({
                                        name: '支付宝',
                                        id: value,
                                        loading: false
                                    });
                                    break;
                                case 'wxpay':
                                    providerList.push({
                                        name: '微信',
                                        id: value,
                                        loading: false
                                    });
                                    break;
                                default:
                                    break;
                            }
                        })
                        this.providerList = providerList;
                    },
                    fail: (e) => {
                        console.log("获取支付通道失败:", e);
                    }
                });
                // #endif
            },
            methods: {
                async requestPayment(e, index) {
                    this.providerList[index].loading = true;
                    let orderInfo = await this.getOrderInfo(e.id);
                    console.log("得到订单信息", orderInfo);
                    if (orderInfo.statusCode !== 200) {
                        console.log("获得订单信息失败", orderInfo);
                        uni.showModal({
                            content: "获得订单信息失败",
                            showCancel: false
                        })
                        return;
                    }
                    uni.requestPayment({
                        provider: e.id,
                        orderInfo: orderInfo.data.data,
                        success: (e) => {
                            console.log("success", e);
                            uni.showToast({
                                title: "感谢您的赞助!"
                            })
                        },
                        fail: (e) => {
                            console.log("fail", e);
                            uni.showModal({
                                content: "支付失败,原因为: " + e.errMsg,
                                showCancel: false
                            })
                        },
                        complete: () => {
                            this.providerList[index].loading = false;
                        }
                    })
                },
                getOrderInfo(e) {
                    let appid = "";
                    // #ifdef APP-PLUS
                    appid = plus.runtime.appid;
                    // #endif
                    let url = 'http://10.10.60.200:8070/sc-admin/sales/wx/prepay/?brokerId=shba01';
                    return new Promise((res) => {
                        uni.request({
                            url: url,
                            success: (result) => {
                                res(result);
                            },
                            fail: (e) => {
                                res(e);
                            }
                        })
                    })
                },
                priceChange(e) {
                    console.log(e.detail.value)
                    this.price = e.detail.value;
                }
            }
        }
    </script>
    
    <style>
        .rmbLogo {
            font-size: 40upx;
        }
    
        button {
            background-color: #007aff;
            color: #ffffff;
        }
    
        .uni-h1.uni-center {
            display: flex;
            flex-direction: row;
            justify-content: center;
            align-items: flex-end;
        }
    
        .price {
            border-bottom: 1px solid #eee;
             200upx;
            height: 80upx;
            padding-bottom: 4upx;
        }
    
        .ipaPayBtn {
            margin-top: 30upx;
        }
    </style>
    

      

    后端代码(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<String, String> result = unifiedOrder(brokerId, clientIP, randomNonceStr);
            System.out.println(request.toString());
            if(StringUtils.isBlank(result.get("prepay_id"))) {
               return AjaxResult.error("支付错误");
            } else {
                logger.info("支付成功");
                Map <String,Object>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, String>  
        (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("<![CDATA[1]]>", "1");
                //xml = xml.replace("__", "_").replace("<![CDATA[", "").replace("]]>", "");
                logger.error(xml);
    
                StringBuffer buffer = HttpUtil.httpsRequest(url, "POST", xml);
                logger.error("unifiedOrder request return body: 
    " + buffer.toString());
                Map<String, String> 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("&notify_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("&timestamp=" + 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://blog.csdn.net/zhuoganliwanjin/article/details/81872215

  • 相关阅读:
    TinyEditor:简洁且易用的html所见即所得编辑器
    arguments.callee 调用自身
    java.io.IOException: 设备未就绪
    关于vcastr3播放器自动播放的问题
    javascript中IE浏览器不支持NEW DATE()带参数的解决方法
    Oracle常用查看表结构命令
    java通过文件头内容判断文件类型
    RS开发日期提示控件默认为昨天
    Cognos更新问题之利用Transform实现Cube增量更新
    SqlServer中从字符串中获取项目指标方法charindex月substring结合
  • 原文地址:https://www.cnblogs.com/smileZAZ/p/14081365.html
Copyright © 2011-2022 走看看