zoukankan      html  css  js  c++  java
  • 微信小程序支付(服务商模式)解决

    前言

    微信支付官方文档: 小程序开发与支付、服务商的关系,参考这个文档

    1、第三方(服务商)自己申请账号,自己开发,生成指定内页给特约商户用,该模式简称中心化模式。

    2、以特约商户身份申请小程序appId,第三方完成开发,该模式简称外包模式。 3、通过开放平台第三方开发者代特约商户进行小程序的开发,该模式简称第三方模式。

    本文适用于中心化模式,服务商自己开发一个小程序,但是收款是直接受到对应的特约商户账户中,不收到服务商自己账户中。

    基本流程

    • 服务商再微信后台申请微信小程序
    • 小程序开通微信支付或绑定已开通微信支付的商户号

    特约商户需要操作的流程

    • 服务商再微信支付商户后台,为特约商户开通微信服务商模式下的微信支付账户
    • 特约商户在收到微信邮件发送的登录账号和密码,登录自己的微信支付商户后台,绑定小程序的appId
    • 提交审核,被拒绝的话再次提交审核,直到审核通过
    • 服务商管理后台中找到"待关联商户号"并确认
    • 登录微信支付服务商商户后台,手动为特约商户绑定与服务商主体或特约商户主体一致的公众号,APP或小程序的appId。最多配置5个,手工配置路径:"服务商商户平台-服务商功能-子商户管理-开发配置-特约商户APPID配置"。

    支付部分

    • appid:注意这里是服务号的appid,不是小程序的
    • mch_id:这里是用服务商的id 在我的账号一栏可以找到
    • sub_appid: 这里才是小程序的appid
    • sub_mch_id: 这里对应特约商户号id 付款到对应商户的凭证就是这个 在注册特约商户的时候邮件里可以找到 这里建议配置到数据库动态传递
    • nonce_str: 随机字符串
    • body: 这里随意填写,也可以填写商品名称
    • out_trade_no: 订单号
    • total_fee: 这里必须是整数,单位是分
    • trade_type: 公众平台支付或小程序支付填写:JSAPI,如果是APP的填写:APP
    • sub_openid: 此参数是在发起支付前在小程序内调起wx.login 方法获得code 然后后台通过置换 获得用户openid
    • spbill_create_ip:这里可以随意填写
    • notify_url: 支付回调的地址
    • sign: 此参数为签名参数 需要将需要传递的参数进行排序并且进行md5签名,需要注意的是需添加参数key 即之前修改的服务商api密钥

    好了 参数分析完毕 在后台调用统一下单方法 不出意外是成功的,下单代码如下:

    Controller部分

    @RestController
    @RequestMapping("/payment")
    public class WxPayController {
    
        private Logger logger = LoggerFactory.getLogger(WxLoginController.class);
    
        @Autowired
        private AppletOrderService appletOrderService;
    
        @Autowired
        private WxPayProperties wxPayProperties;
    
        @ResponseBody
        @PostMapping(value = "/appletWxPay", produces = "application/json;charset=UTF-8")
        public Map<Object, Object> appletWxPay(@RequestParam String openId, String totalFee) throws Exception {
            logger.info("[WxPayController].appletWxPay...openId:{}", openId);
            SortedMap<Object, Object> resultMap = new TreeMap<Object, Object>();
            String body = "测试";
            String out_trade_no = String.valueOf(IdWorker.getInstance().nextId());
            PreOrderResult preOrderResult = appletOrderService.placeOrder(body, out_trade_no, totalFee, openId);
            System.out.println(preOrderResult);
    
            if(WxContants.SUCCESS.equals(preOrderResult.getReturn_code()) && WxContants.SUCCESS.equals(preOrderResult.getResult_code())){
                resultMap.put("appId", wxPayProperties.getApp_id());
                resultMap.put("timeStamp", Long.toString(System.currentTimeMillis()/1000));
                resultMap.put("nonceStr", UUID.randomUUID().toString().replaceAll("-", "").toUpperCase());
                resultMap.put("package", "prepay_id="+preOrderResult.getPrepay_id());
                resultMap.put("signType", "MD5");
                resultMap.put("sign", SignUtils.createSignByMd5(resultMap, wxPayProperties.getKey()));
                resultMap.put("returnCode", "SUCCESS");
                resultMap.put("returnMsg", "OK");
                logger.info("【小程序支付】统一下单成功,返回参数:"+resultMap);
            }else{
                resultMap.put("returnCode", preOrderResult.getReturn_code());
                resultMap.put("returnMsg", preOrderResult.getReturn_msg());
                logger.info("【小程序支付】统一下单失败,失败原因:{}" + preOrderResult.getReturn_msg());
            }
            logger.info("[WxPayController].appletWxPay...CodeUrl:{}", preOrderResult.getCode_url());
            return resultMap;
        }
    
    }
    

    Serivice部分

    @Service
    public class AppletOrderServiceImpl implements AppletOrderService{
    
        @Autowired
        private WxPayProperties wxPayProperties;
    
        @Override
        public PreOrderResult placeOrder(String body, String out_trade_no, String total_fee, String openId) throws Exception {
            // 生成预付单对象
            PreOrder preOrder = new PreOrder();
            preOrder.setAppid(wxPayProperties.getApp_id());
            preOrder.setMch_id(wxPayProperties.getMch_id());
            preOrder.setSub_appid(wxPayProperties.getSub_app_id());
            preOrder.setSub_mch_id(wxPayProperties.getSub_mch_id());
            String nonce_str = UUID.randomUUID().toString().replaceAll("-", "").toUpperCase();
            preOrder.setNonce_str(nonce_str);
    
            preOrder.setBody(body);
            preOrder.setOut_trade_no(out_trade_no);
    
            preOrder.setTotal_fee(new BigDecimal(total_fee));
            preOrder.setSpbill_create_ip(wxPayProperties.getSpbill_create_ip());
            preOrder.setNotify_url(wxPayProperties.getNotify_url());
            preOrder.setTrade_type(WxContants.TRADE_TYPE);
            preOrder.setSub_openid(openId);
            SortedMap<Object, Object> p = new TreeMap<Object, Object>();
            p.put("appid", wxPayProperties.getApp_id());
            p.put("mch_id", wxPayProperties.getMch_id());
            p.put("sub_appid", wxPayProperties.getSub_app_id());
            p.put("sub_mch_id", wxPayProperties.getSub_mch_id());
            p.put("body", body);
            p.put("nonce_str", nonce_str);
            p.put("out_trade_no", out_trade_no);
            p.put("total_fee", total_fee);
            p.put("spbill_create_ip", wxPayProperties.getSpbill_create_ip());
            p.put("notify_url", wxPayProperties.getNotify_url());
            p.put("trade_type", WxContants.TRADE_TYPE);
            p.put("sub_openid", openId);
            // 签名
            String sign = SignUtils.createSignByMd5(p, wxPayProperties.getKey());
            preOrder.setSign(sign);
            String xml = XmlUtil.object2Xml(preOrder, PreOrder.class);
            // 调用下单地址
            String returnXml = HttpUtil.sendPost(WxContants.pay_url, xml);
            System.out.println(returnXml);
            // XML转换为Object
            PreOrderResult preOrderResult = (PreOrderResult) XmlUtil.xml2Object(returnXml, PreOrderResult.class);
            // XML转换为Object
            // 一般企业开发中支付流水入库,支付状态更新这些都需要做,此出省略
            return preOrderResult;
        }
    
        @Override
        public PayResult getWxPayResult(HttpServletRequest request) throws Exception {
            InputStream inStream = request.getInputStream();
            BufferedReader in = null;
            String result = "";
            in = new BufferedReader(
                    new InputStreamReader(inStream));
            String line;
            while ((line = in.readLine()) != null) {
                result += line;
            }
            PayResult pr = (PayResult)XmlUtil.xml2Object(result, PayResult.class);
            System.out.println(pr.toString());
            return pr;
        }
    
    }
    

    小程序配置部分

    @Component
    public class WxPayProperties {
    
        @Value("${wxpay.app_id}")
        private String app_id;
    
        @Value("${wxpay.sub_app_id}")
        private String sub_app_id;
    
        @Value("${wxpay.spbill_create_ip}")
        private String spbill_create_ip;
    
        @Value("${wxpay.key}")
        private String key;
    
        @Value("${wxpay.mch_id}")
        private String mch_id;
    
        @Value("${wxpay.sub_mch_id}")
        private String sub_mch_id;
    
        @Value("${wxpay.notify_url}")
        private String notify_url;
    
        // 此处省略get/set方法
        ...
            
    }
    

    返回数据

    {
        "appId": "wxe670bb9ea4775345",
        "nonceStr": "536D9056202D4292A909392320E2E5BB",
        "package": "prepay_id=wx13143641616855cfa3275610dd2a070000",
        "returnCode": "SUCCESS",
        "returnMsg": "OK",
        "sign": "C512D4025134C356BFA58A2F5699E198",
        "signType": "MD5",
        "timeStamp": "1610519802"
    }
    

    小程序端根据后台返回的参数,拉起支付,代码如下:

    wx.requestPayment({
    	'timeStamp': res.data.timeStamp,
    	'nonceStr': res.data.nonceStr,
    	'package': res.data.package,
    	'signType': res.data.signType,
    	'paySign': res.data.sign,
    	'success':function(res){},
    	'fail':function(res){},
    	'complete':function(res){}
    })
    

    点击支付,总算是来到了这一步:

    image-20210113144740860

    过程中,可能会遇到如下问题:

    https://note.youdao.com/yws/api/personal/file/75A86A993B104245B319B1F2D7B53BFE?method=download&shareKey=295148895adc01c00c7578849dd50e26

    出现这个错误的原因是签名不正确,多检查检查是哪一步出现了问题。

    最后

    一路踩了不少坑,总算还是成功了,因此将解决方法记录下来,后面做小程序支付功能的小伙伴可以避免踩坑。

    __EOF__

    作  者:Jerry
    出  处:https://www.cnblogs.com/jerry0612/p/14272073.html
    关于博主:编程路上的小学生,热爱技术,喜欢专研。评论和私信会在第一时间回复。或者直接私信我。
    版权声明:署名 - 非商业性使用 - 禁止演绎,协议普通文本 | 协议法律文本
    声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐】一下。您的鼓励是博主的最大动力!

     
     
  • 相关阅读:
    Pacbio三代基因组组装简介
    微生物组与代谢组关联分析
    软件架构学习小结
    Android中TextView中加图片,超链接,部分字或者背景变色。。。不断补充中
    Android 使用 ViewPager 实现左右滑动 Fragment
    Android 使用 ViewPager 实现左右滑动 View
    Android 使用 ViewPager 实现左右循环滑动图片
    Fragment 与 Activity 通信
    Android App 组件之 ListFragment
    Android 官方推荐: DialogFragment 创建对话框
  • 原文地址:https://www.cnblogs.com/javalinux/p/14760354.html
Copyright © 2011-2022 走看看