您现在的位置是:主页 > news > 北滘网站设计/肇庆网站快速排名优化

北滘网站设计/肇庆网站快速排名优化

admin2025/4/23 17:30:39news

简介北滘网站设计,肇庆网站快速排名优化,深圳专业商城网站制作,做教育类网站一般流程前言“微信小程序在开发中大部分都是电商类的业务,电商必不可少的是支付环节。实现微信小程序支付看微信官方文档太晦涩(多数人都觉得官方人员写的烂),我也是在看官方文档的路上迷失去了自己。这篇文章是我实战采坑后总结的经验构成,尽量带着…

北滘网站设计,肇庆网站快速排名优化,深圳专业商城网站制作,做教育类网站一般流程前言“微信小程序在开发中大部分都是电商类的业务,电商必不可少的是支付环节。实现微信小程序支付看微信官方文档太晦涩(多数人都觉得官方人员写的烂),我也是在看官方文档的路上迷失去了自己。这篇文章是我实战采坑后总结的经验构成,尽量带着…

前言

微信小程序在开发中大部分都是电商类的业务,电商必不可少的是支付环节。实现微信小程序支付看微信官方文档太晦涩(多数人都觉得官方人员写的烂),我也是在看官方文档的路上迷失去了自己。这篇文章是我实战采坑后总结的经验构成,尽量带着大家读懂官方文档,授人以鱼不如授人以渔。

微信小程序支付必须有商户号,绑定商户平台才有微信支付功能,个人小类型程序没有支付功能。商户平台可使用商户营业执照申请

一、支付流程导读

1.支付系统交互流程

在开发前若时间充足,建议大家通篇阅读一下微信支付官方文档[1],对业务名称加以熟悉,看清楚接口参数名,是否必传,方便后面文章的阅读与开发。

5a272e207c62376cd81001ced038830f.png
微信支付流程图
  1. 调用微信登录API接口获取用户openid。
  2. 将支付金额,回调地址,小程序appid等传入调用统一下单接口API。
  3. 微信官方生成预支付订单数据,包括订单id,支付金额等。
  4. 将返回数据进行签名,防止篡改。
  5. 将5个参数数据返回给前端调用Payment(输密码的面板)。
  6. 用户确认金额,输入密码。
  7. 微信进行密码鉴权。
  8. 微信回调你的业务系统,将支付是否成功通知给你,进行订单支付状态更新。此时你的接口必须放开权限,且公网可访问。

二、开发前的准备

1.准备资料

  1. appid :小程序的id
  2. apikey : 可在小程序开发配置找到
  3. mchid : 商户号,商户平台可找到

2.引入SDK

微信支付官方给我们提供了很多工具类。微信支付SDK[2]中有方法可以在没有业务订单数据的情况下,给你生成订单参数,实现跑通微信支付,给大家提供的一个测试类。很友好,里面提供了支付时需要用的工具类方法。

3. 小程序配置合法域名

小程序必须配置合法请求域名地址,外部公网才能请求访问小程序接口。具体步骤为:小程序管理员打开微信公众平台,扫描登录微信小程序,进入小程序开发配置,配置小程序域名地址。

4a936447dc6f5af0664c09cbd46e8afe.png
小程序合法域名配置

4 申请微信商户号

微信支付商户号类似于银行卡,充当收款账户功能。由于文章篇幅有限,此文不作讲解,可以查看申请微信商户号[3]官方文档教程进行申请。

5 小程序与商户号关联

申请微信小程序以后,将小程序APPID与微信商户号绑定,通过小程序收到的资金就进入指定的商户中心。小程序绑定商户号文档[4]讲解的很详细,这里就不再赘述,翻阅至文章结尾可看到教程地址。


三、获取用户OpenId

1. 配置文件

weixin:
  # 小程序ID  
  appid: wx08huxxxxxxxxxxx
  # 小程序秘钥
  appSecret: 95e2932xxxxxxxxxxxx
  # 商户秘钥
  apiKey: ymruurG3xxxxxxxxxx
   # 商户ID
  mchId: 1244xxxxxxxx
#支付回调通知地址 必须为公网可访问的域名地址 且权限放开
routine:
  notify: https:xxxxxx/wx/pay/notify

2. 获取用户OpenID

首先前端同学需要请求微信服务获取一个登录code[5]。然后带着登录code请求后端接口

    @GetMapping(value = "/login/{code}")
    @ApiOperation(value = "获取用户openid" , response = ApiResult.class)
    @ResponseBodypublic ApiResult login(@PathVariable String code) throws Exception {
        Map result= wxAuthApiService.wxLogin(code);if (result.isEmpty()){return ApiResult.fail("获取用户小程序openid失败");
        }return ApiResult.ok(result);
    }

Service

public interface WxAuthApiService {
 // 获取获取用户小程序openid
    Map wxLogin(String code) throws Exception;
}

实现类

/**
 * @Author zhenxiang
 * @Version 1.0
 * @Description
 */
@Slf4j
@Service
public class WxAuthApiServiceImpl implements WxAuthApiService {
   // 小程序ID
   @Value("${weixin.appid}")
    private String APPID;
    // 小程序秘钥
    @Value("${weixin.appSecret}")
    private String SECRET;
    
  @Transactional(rollbackFor = Exception.class)
    @Overridepublic Map<String, String> wxLogin(String code) throws Exception {
        Map param = new ConcurrentHashMap<>();if (StringUtils.isEmpty(code)) {throw new BusinessException("获取用户openid失败,code 不能为空");
        }
        String url = String.format("https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code",
                APPID, SECRET, code);
        String result = HttpUtils.sendGet(url, null);
        log.info(result);
        JSONObject object = JSON.parseObject(result);if (object.getInteger("errcode") != null) {
            log.info("获取用户openid失败code:{},msg:{}", object.getInteger("errcode"), object.getString("errmsg"));throw new BusinessException("获取用户openid失败,errcode:" + object.getInteger("errcode") + "errmsg:" + object.getString("errmsg"));
        }
        String openId = object.getString("openid");// 会话密钥
        String sessionKey = object.getString("session_key");// 根据业务将openid与sessionKey 持久化存入数据库
   param.put("openid", openId);
        param.put("sessionKey", sessionKey);return param;
  }

顺利拿到OpenID[6] 以后就可以请求支付,因为微信支付必须确定订单的支付人OpenID。


四、支付流程

1. 支付接口

/**
     * 微信小程序 统一下单支付
     * @param request
     * @param response
     * @return
     */
    @PostMapping("/unifiedOrder")
    @ResponseBody
    @ApiOperation(value = "微信统一下单", response = ApiResult.class)public ApiResult unifiedOrder(HttpServletRequest request, HttpServletResponse response) throws Exception {
        log.info("---------统一下单--------");
        String openId = request.getParameter("openId");
        if (StringUtils.isEmpty(openId)) {
            throw  new BusinessException("openid 不能为空");
        }
        log.info("---------openId--------" + openId);
        Map map = wxPayService.unifiedOrder(openId, IpUtil.getIpAddress(request), request);return ApiResult.ok(map);
    }

service

   /**
     *  统一下单接口
     * @param openid 用户标识
     * @param ip    请求ip
     * @param request 订单数据
     * @return
     */
    Map unifiedOrder(String openid, String ip, HttpServletRequest request) throws Exception;

实现类

/**
 * @Author zhenxiang
 * @Version 1.0
 * @Description
 */
@Service
@Slf4j
public class WxPayServiceImpl implements WxPayService {

    @Value("${weixin.appid}")
    private String APPID;
    @Value("${weixin.appSecret}")
    private String SECRET;
    @Value("${weixin.apiKey}")
    private String APIKEY;
    @Value("${weixin.mchId}")
    private String MCHID;

  //交易类型
    public static final String TRADE_TYPE_JS = "JSAPI";
    public static final String FAIL = "FAIL";
    public static final String SUCCESS = "SUCCESS";
   // 微信统一下单url
    public static final String UNIFIED_ORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
    // 支付回调地址
    @Value("${routine.notify}")
    public String NOTIFY_URL;
    
     /** 统一下单
     * @param openid 用户唯一标识
     * @param ip 请求方IP地址
     * @param request 请求数据
     * @return 已生成订单信息
     */
    @Override
    public Map unifiedOrder(String openid, String ip, HttpServletRequest request) throws Exception {
        // 1. 拼接统⼀下单地址参数
        log.info("进入下单流程");
        Map paramMap = new ConcurrentHashMap(10);
        paramMap.put("appid", APPID);
        paramMap.put("mch_id", MCHID);// 商品信息
        paramMap.put("body", "test");
        paramMap.put("openid", openid);// 订单号
        paramMap.put("out_trade_no", request.getParameter("orderId"));// 随机字符串,长度要求在32位以内。
        paramMap.put("nonce_str", StringHelpUtils.generateNonceStr());// 以分为单位,1代表0.01元
        paramMap.put("total_fee", "1");
        paramMap.put("spbill_create_ip", ip);
        paramMap.put("notify_url", NOTIFY_URL);// 支付类型
        paramMap.put("trade_type", TRADE_TYPE_JS);// 微信官方SDK中生成签名方法
        String paramXml = WxPayUtil.generateSignedXml(paramMap, APIKEY, WxPayUtil.SignType.MD5);
        log.info("签名参数:{}", paramXml);
        Map resultMap = new ConcurrentHashMap<>(10);// 请求微信服务发起支付 生成预支付订单ID
        String returnXml = HttpClientUtil.httpsRequest(UNIFIED_ORDER_URL, "POST", paramXml);
        log.info("返回参数:{}", returnXml);
        resultMap = WxPayUtil.xmlToMap(returnXml);
        Map returnMap = new ConcurrentHashMap<>();// 微信SDK方法验证签名 再次签名if (WxPayUtil.isSignatureValid(resultMap, APIKEY, WxPayUtil.SignType.MD5)) {// 签名成功后返给前端
            returnMap.put("appId", APPID);// 时间戳
            returnMap.put("timeStamp", WXPayUtil.getCurrentTimestamp() + "");// 随机字符串
            returnMap.put("nonceStr", WXPayUtil.generateNonceStr());// 签名类型
            returnMap.put("signType", WXPayConstants.MD5);// 微信预订单ID ,package参数名在微信文档中已指定必填
            returnMap.put("package", "prepay_id=" + resultMap.get("prepay_id"));// 5. 通过appId, timeStamp, nonceStr, signType, package及商户密钥进⾏key=value形式拼接并加密
            String paySign = WXPayUtil.generateSignature(returnMap, APIKEY, WXPayConstants.SignType.MD5);
            returnMap.put("paySign", paySign);
            returnMap.put("mchId", MCHID);
            returnMap.put("resultCode", SUCCESS);
            log.info("returnMap:{}", returnMap);return returnMap;
        } else {
            returnMap.put("resultCode", FAIL);
            log.info("签名验证错误");
        }return returnMap;
    }
}

前端同学拿到以上参数就能唤起Payment 支付密码输入面板啦。

0a79b98f8456fb2e147c299c10b174fb.png
Payment支付确认

2. 注意事项

  • 微信支付金额以“分”为单位,需将"元"转换为"分"。
  • 微信支付金额不能为"0"元。0元将导致签名失败,无法唤起Payment。

以上给我记牢了,我曾经一次又一次在这翻车,一步步看日志的那种,唉不说了,一把辛酸泪。


五 、支付回调

何所谓支付回调:订单支付钱到了微信,究竟是否支付成功,业务系统无从知道。微信官方提供一个消息推送接口,下单后,将会请求你订单支付时设置的参数回调接口地址。微信会传送订单号、支付是否成功、支付金额、时间等信息。根据信息我们对相应的订单进行业务处理。

上代码。

接口:

 /**
     * 通知回调 notify
     * @param request 订单信息
     * @param response
     * @return  SUCCESS/FAIL
     * @throws Exception
     */
    @RequestMapping(value = "/notify")
    @ApiOperation(value = "微信支付回调", response = ApiResult.class)public void notify(HttpServletRequest request, HttpServletResponse response) throws Exception {
        log.info("微信开始回调");
        String  resXml = wxPayService.notify(request,response);
        log.info("微信回调成功,返回值:{}",resXml);
    }

实现类:

 /** 支付回调
     * @param request
     * @param response
     * @return
     */
    @Override
    public String notify(HttpServletRequest request, HttpServletResponse response) throws IOException {
        log.info("---------------微信支付回调----------------------");
        // 调用返回 xml
        String resXml = "";
        try {
            // 解析request中xml 转换成map
            Map map = WxPayUtil.parseXml(request);
            log.info("---------------微信支付回调订单号为:{}--------", map.get("out_trade_no"));// 检验签名是否成功if (WxPayUtil.isSignatureValid(map, APIKEY, WxPayUtil.SignType.MD5)) {
                log.info("微信支付return_code:{}", map.get("return_code"));//log.info("判断结果:{}", "SUCCESS".equals(map.get("return_code")));if ("SUCCESS".equals(map.get("return_code"))) {
                    Boolean result = orderService.orderCallback(map).getData();
                    log.info("支付回调得到order更新数据库结果:{}", result);if (result) {// 返回success给微信服务器 剔除推送队列中的本次交易
                        resXml = " " +"";
                    } else {
                        log.info("----------------微信回调成功 更新数据库状态失败---------------");
                    }
                } else {
                    log.info("微信回调失败:{}", map.get("return_code"));
                }
            } else {
                 resXml = "" + "" + "" + " ";
            }
        } catch (Exception e) {
            log.error("微信回调失败,抛出异常:{}", e);
        }
        PrintWriter printWriter = new PrintWriter(response.getOutputStream());
        log.info("回调返回给微信服务器:{}", resXml);
        printWriter.write(resXml);
        printWriter.flush();
        printWriter.close();return resXml;
    }

一定要放开支付回调的权限啊,不然微信想回调你,也进不了你家大门啊

以上就是微信小程序支付实现的大致过程,下期讲 微信退款。

cb8e2c43f4595df387c5a60e484b502d.png

参考资料

[1]

微信支付官方文档:https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=2_1

[2]

微信支付SDK文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=11_1

[3]

微信商户号文档地址文档:https://kf.qq.com/faq/180910jimEvQ180910Zj6jQV.html

[4]

小程序绑定商户号文档:https://kf.qq.com/faq/1801116VJfua1801113QVNVz.html

[5]

登录code文档:https://developers.weixin.qq.com/miniprogram/dev/api/open-api/login/wx.login.html

[6]

什么是OpenID:OpenID是网站上或应用中唯一对应用户身份的标识,网站或应用可将此ID进行存储,便于用户下次登录时辨识其身份,或将其与用户在网站上或应用中的原有账号进行绑定


写在最后

由于微信公众号受不可抗拒原因,关闭了新注册公众号的文末留言功能,所以导致我无法与读者进行讨论交流,后期可能会开放。想跟作者深入沟通的进入微信公众号内留言,知无不言。

点赞、在看、你的支持是我写作的动力。不支持? 年轻人不讲武德。

如果能分享,帮助到你圈子里,更多想掌握对接微信支付的朋友的话,那就太感谢观众老爷啦。

4b2ac577005f03a5b65ad85fb0021dea.png
长按关注我