• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

微信小程序支付Java服务端开发源码,及那些你不知道的坑(一) ...

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

受新冠病毒疫情影响,小程序又被推上风间浪头,曾经的线下实体企业都开始纷纷的转型线上,但目前线上最大的入口莫过于微信。因此小程序成了商家们转型线上的首选。而由于微信自己的生态原因,小程序的在线支付只能使用微信小程序支付。这有让微信支付也越来越火,最近有很多开发者都找我咨询和要微信支付的源码的事情。我今天也再说说这事。

微信小程序支付

说道小程序支付,我要稍稍吐槽一下,微信支付真的搞的很乱。如果你之前项目中已经接入了微信的扫码和App支付,现在要接入小程序支付,大多数人的想法就是,我已经有微信支付的账号了,直接接入使用,那我告诉你,你错了。微信小程序支付是通过申请的小程序开通的微信支付,而微信的扫码和App支付是通过公众号开通的支付,两个不可通用。真是够麻烦,相当让人反感,但我们还得乖乖的用,谁叫人家微信有如此大的生态呢?

不了解微信小程序支付的伙伴们,建议还是先看看开发者文档,知道基础的业务流程。

小程序支付的业务流程图:

一,小程序支付开发源码

首先创建自己的项目,我这里创建的是SpringBoot的项目。

1,在resources下创建application.yml文件,并添加配置如下:

 1 spring:
 2   profiles:
 3     active: dev
 4  
 5 ##pay config
 6 payment:
 7   ##wechat config
 8   wx:
 9     ##小程序支付
10     lte:
11       appid: ***
12       mchid: ***
13       key: ***

2,创建获取配置属性类PayConfig,代码如下:

 1 @Component
 2 @ConfigurationProperties(prefix = "payment")
 3 public class PayConfig {
 4  
 5     //微信支付类型
 6     //NATIVE--原生支付
 7     public static final String TRADE_TYPE_NATIVE = "NATIVE";
 8     //JSAPI--公众号支付-小程序支付
 9     public static final String TRADE_TYPE_JSAPI = "JSAPI";
10     //MWEB--H5支付
11     public static final String TRADE_TYPE_MWEB = "MWEB";
12     //APP -- app支付
13     public static final String TRADE_TYPE_APP = "APP";
14  
15  
16     //小程序支付参数
17     public static String WX_LTE_APP_ID;
18     public static String WX_LTE_MCH_ID;
19     public static String WX_LTE_KEY;
20  
21     
22     //微信支付API
23     public static final String WX_PAY_UNIFIED_ORDER = "https://api.mch.weixin.qq.com/pay/unifiedorder";
24  
25  
26     @Value("${payment.wx.lte.appid}")
27     public void setWxLteAppId(String wxLteAppId) {
28         WX_LTE_APP_ID = wxLteAppId;
29     }
30     @Value("${payment.wx.lte.mchid}")
31     public void setWxLteMchId(String wxLteMchId) {
32         WX_LTE_MCH_ID = wxLteMchId;
33     }
34     @Value("${payment.wx.lte.key}")
35     public void setWxLteKey(String wxLteKey) {
36         WX_LTE_KEY = wxLteKey;
37     }
38 }

3,添加启动类:

 

4,创建一个常量类,放支付涉及的常量信息

 1 public interface PaymentConstants {
 2  
 3     String COMPANY_NAME = "某某某科技有限公司";//用做收款方名称
 4     String COMPANY_NICK_NAME = "某某某";//收款方名称简称
 5     String COMPANY_PREFIX = "pay_";
 6  
 7     //项目环境
 8     String PROJECT_ENV_DEV = "dev";
 9     String PROJECT_ENV_PRO = "pro";
10  
11     String PAYMENT_TITLE = "商品购买";//支付title信息,也可用真实商品名称
12  
13     //支付类型,支付宝和微信
14     int PAY_TYPE_ALI = 1;
15     int PAY_TYPE_WX = 2;
16  
17     //微信支付成功后回调url
18     String WX_PAY_CALLBACK_URL = "/api/payment/wxNotify";
19  
20     //扫描支付
21     String PAY_TRADE_TYPE_QR = "QR";
22     //App支付
23     String PAY_TRADE_TYPE_APP = "APP";
24     //小程序支付
25     String PAY_TRADE_TYPE_LTE = "LTE";
26  
27     String SUCCESS = "SUCCESS";
28     String OK = "OK";
29 }

5,创建服务接口PaymentService;

 1 public interface PaymentService {
 2  
 3     /**
 4      * 小程序支付
 5      * @param openId
 6      * @param orderNo
 7      * @param money
 8      * @return
 9      * @throws Exception
10      */
11     Map<String, String> wxLtePayment(String openId, String orderNo, double money) throws Exception;
12  
13     /**
14      * 微信支付回调
15      * @param map
16      * @return
17      * @throws Exception
18      */
19     int wxNotify(Map<String, Object> map) throws Exception;
20  
21  
22     PaymentRecord queryPaymentStatusById(String orderNo);
23  
24 }

6,创建服务接口PaymentServiceImpl;

  1 @Service
  2 public class PaymentServiceImpl implements PaymentService {
  3  
  4     private static Logger LOGGER = LoggerFactory.getLogger(PaymentServiceImpl.class);
  5  
  6     @Value("${spring.profiles.active}")
  7     private String PROJECT_ENV;
  8  
  9     @Value("${server.domain}")
 10     private String SERVER_DOMAIN;
 11     
 12     @Autowired
 13     private PaymentRecordMapper paymentRecordMapper;
 14     
 15  
 16     @Override
 17     @Transactional(readOnly=false,rollbackFor={Exception.class})
 18     public Map<String, String> wxLtePayment(String openId, String orderNo, double money) throws Exception {
 19         LOGGER.info("【小程序支付】 统一下单开始, 订单编号="+orderNo);
 20         SortedMap<String, String> resultMap = new TreeMap<String, String>();
 21         //生成支付金额
 22         double payAmount = PayUtil.getPayAmountByEnv(PROJECT_ENV, money);
 23         //添加或更新支付记录
 24         int flag = this.addOrUpdatePaymentRecord(orderNo, payAmount, PaymentConstants.PAY_TYPE_WX, PaymentConstants.PAY_TRADE_TYPE_LTE, false, null);
 25         if(flag < 0){
 26             resultMap.put("returnCode", "FAIL");
 27             resultMap.put("returnMsg", "此订单已支付!");
 28             LOGGER.info("【小程序支付】 此订单已支付!");
 29         }else if(flag == 0){
 30             resultMap.put("returnCode", "FAIL");
 31             resultMap.put("returnMsg", "支付记录生成或更新失败!");
 32             LOGGER.info("【小程序支付】 支付记录生成或更新失败!");
 33         }else{
 34             Map<String,String> resMap = this.wxUnifieldOrder(orderNo, PayConfig.TRADE_TYPE_JSAPI, payAmount,false, openId);
 35             if(PaymentConstants.SUCCESS.equals(resMap.get("return_code")) && PaymentConstants.SUCCESS.equals(resMap.get("result_code"))){
 36                 resultMap.put("appId", PayConfig.WX_LTE_APP_ID);
 37                 resultMap.put("timeStamp", PayUtil.getCurrentTimeStamp());
 38                 resultMap.put("nonceStr", PayUtil.makeUUID(32));
 39                 resultMap.put("package", "prepay_id="+resMap.get("prepay_id"));
 40                 resultMap.put("signType", "MD5");
 41                 resultMap.put("sign", PayUtil.createSign(resultMap,PayConfig.WX_LTE_KEY));
 42                 resultMap.put("returnCode", "SUCCESS");
 43                 resultMap.put("returnMsg", "OK");
 44                 LOGGER.info("【小程序支付】统一下单成功,返回参数:"+resultMap);
 45             }else{
 46                 resultMap.put("returnCode", resMap.get("return_code"));
 47                 resultMap.put("returnMsg", resMap.get("return_msg"));
 48                 LOGGER.info("【小程序支付】统一下单失败,失败原因:"+resMap.get("return_msg"));
 49             }
 50         }
 51         return resultMap;
 52     }
 53  
 54     @Override
 55     @Transactional(readOnly=false,rollbackFor={Exception.class})
 56     public int wxNotify(Map<String,Object> map) throws Exception{
 57         Integer flag = 0;
 58         //支付订单编号
 59         String orderNo = (String)map.get("out_trade_no");
 60         //检验是否需要再次回调刷新数据
 61         if(this.isNotifyAgain(orderNo)){
 62             PaymentRecordExample example = new PaymentRecordExample();
 63             example.createCriteria().andOrderNoEqualTo(orderNo);
 64             example.setOrderByClause("id DESC");
 65             List<PaymentRecord> list = paymentRecordMapper.selectByExample(example);
 66             if(list!=null && list.size()>0){
 67                 //当前时间
 68                 Date currentTime = new Date();
 69                 PaymentRecord record = list.get(0);
 70                 record.setTradeNo(String.valueOf(map.get("transaction_id")));
 71                 record.setStatus(Boolean.TRUE);
 72                 record.setUpdateTime(currentTime);
 73                 //更新条件
 74                 PaymentRecordExample where = new PaymentRecordExample();
 75                 where.createCriteria().andRecordIdEqualTo(record.getRecordId()).andStatusEqualTo(Boolean.FALSE);
 76                 flag = paymentRecordMapper.updateByExampleSelective(record,where);
 77                 LOGGER.info("【微信充值回调】 记录更新成功,订单值ID="+orderNo);
 78                 if(flag > 0){
 79                     PaymentNotify paymentNotify = new PaymentNotify();
 80                     paymentNotify.setRecordId(record.getRecordId());
 81                     paymentNotify.setOrderNo(record.getOrderNo());
 82                     paymentNotify.setTradeNo(record.getTradeNo());
 83                     paymentNotify.setCreateTime(new Date());
 84                     paymentNotify.setStatus(true);
 85                     if(paymentNotifyMapper.insert(paymentNotify) > 0){
 86                         LOGGER.info("【微信支付回调】 提醒信息生成成功!");
 87                     }
 88                 }else{
 89                     PaymentNotify paymentNotify = new PaymentNotify();
 90                     paymentNotify.setRecordId(record.getRecordId());
 91                     paymentNotify.setOrderNo(record.getOrderNo());
 92                     paymentNotify.setTradeNo(record.getTradeNo());
 93                     paymentNotify.setCreateTime(new Date());
 94                     paymentNotify.setStatus(false);
 95                     if(paymentNotifyMapper.insert(paymentNotify) > 0){
 96                         LOGGER.info("【微信支付回调】 提醒信息生成成功!");
 97                     }
 98                 }
 99                 LOGGER.info("【微信支付回调】 订单支付成功,订单号:"+orderNo);
100             }
101         }
102         return flag;
103     }
104  
105  
106     @Override
107     @Transactional(readOnly=true,rollbackFor={Exception.class})
108     public PaymentRecord queryPaymentStatusByNo(String OrderNo){
109         PaymentRecordExample example = new PaymentRecordExample();
110         example.createCriteria().andOrderNoEqualTo(OrderNo);
111         example.setOrderByClause("id DESC");
112         List<PaymentRecord> list = paymentRecordMapper.selectByExample(example);
113         if(list != null && list.size()>0 && list.get(0).getStatus()){
114             return list.get(0);
115         }
116         return null;
117     }
118     /**
119      * <p>微信支付统一下单</p>
120      *
121      * @param orderNo 订单编号
122      * @param tradeType 支付类型
123      * @param payAmount 支付类型
124      * @param noLtePay 非小程序支付
125      * @return
126      * @throws Exception
127      */
128     private Map<String,String> wxUnifieldOrder(String orderNo, String tradeType, double payAmount, boolean noLtePay, String openid) throws Exception{
129         //封装参数
130         SortedMap<String,String> paramMap = new TreeMap<String,String>();
131         String appId = noLtePay?PayConfig.WX_APP_ID:PayConfig.WX_LTE_APP_ID;
132         String mchId = noLtePay?PayConfig.WX_MCH_ID:PayConfig.WX_LTE_MCH_ID;
133         paramMap.put("appid", appId);
134         paramMap.put("mch_id", mchId);
135         paramMap.put("nonce_str", PayUtil.makeUUID(32));
136         paramMap.put("body", PaymentConstants.COMPANY_NAME);
137         paramMap.put("out_trade_no", orderNo);
138         paramMap.put("total_fee", PayUtil.moneyToIntegerStr(payAmount));
139         paramMap.put("spbill_create_ip", PayUtil.getLocalIp());
140         paramMap.put("notify_url", this.getNotifyUrl(PaymentConstants.PAY_TYPE_WX));
141         paramMap.put("trade_type", tradeType);
142         if (!noLtePay) {
143             paramMap.put("openid",openid);
144         }
145         String payKey = noLtePay?PayConfig.WX_KEY:PayConfig.WX_LTE_KEY;
146         paramMap.put("sign", PayUtil.createSign(paramMap,payKey));
147         //转换为xml
148         String xmlData = PayUtil.mapToXml(paramMap);
149         //请求微信后台,获取预支付ID
150         String resXml = HttpUtils.postData(PayConfig.WX_PAY_UNIFIED_ORDER, xmlData);
151         LOGGER.info("【微信支付】 统一下单响应:\n"+resXml);
152         return PayUtil.xmlStrToMap(resXml);
153     }
154     
155     /**
156      * <p>添加或更新支付记录</p>
157      *
158      * @param orderNo
159      * @param payAmount
160      * @param payType
161      * @return
162      * @throws Exception 
163      */
164     private int addOrUpdatePaymentRecord(String orderNo, double payAmount, int payType, String tradeType, boolean isPayment, String tradeNo) throws Exception{
165         //添加或更新数据库的支付记录逻辑
166         // 写自己的实现代码
167     }
168 }

7,支付工具栏PayUtil

  1 public class PayUtil {
  2     static Logger log = LogManager.getLogger(PayUtil.class.getName());
  3     /**
  4      * 获取当前机器的ip
  5      *
  6      * @return String
  7      */
  8     public static String getLocalIp(){
  9         InetAddress ia=null;
 10         String localip = null;
 11         try {
 12             ia=ia.getLocalHost();
 13             localip=ia.getHostAddress();
 14         } catch (Exception e) {
 15             e.printStackTrace();
 16         }
 17         return localip;
 18         
 19     }
 20     
 21     /**
 22      * Map转换为 Xml
 23      * 
 24      * @param map
 25      * @return Xml
 26      * @throws Exception
 27      */
 28     public static String mapToXml(SortedMap<String, String> map) throws Exception {
 29         DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
 30         //防止XXE攻击
 31         documentBuilderFactory.setXIncludeAware(false);
 32         documentBuilderFactory.setExpandEntityReferences(false);
 33         DocumentBuilder documentBuilder= documentBuilderFactory.newDocumentBuilder();
 34         org.w3c.dom.Document document = documentBuilder.newDocument();
 35         org.w3c.dom.Element root = document.createElement("xml");
 36         document.appendChild(root);
 37         for (String key: map.keySet()) {
 38             String value = map.get(key);
 39             if (value == null) {
 40                 value = "";
 41             }
 42             value = value.trim();
 43             org.w3c.dom.Element filed = document.createElement(key);
 44             filed.appendChild(document.createTextNode(value));
 45             root.appendChild(filed);
 46         }
 47         TransformerFactory tf = TransformerFactory.newInstance();
 48         Transformer transformer = tf.newTransformer();
 49         DOMSource source = new DOMSource(document);
 50         transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
 51         transformer.setOutputProperty(OutputKeys.INDENT, "yes");
 52         StringWriter writer = new StringWriter();
 53         StreamResult result = new StreamResult(writer);
 54         transformer.transform(source, result);
 55         String output = writer.getBuffer().toString();
 56         try {
 57             writer.close();
 58         }
 59         catch (Exception ex) {
 60         }
 61         return output;
 62     }
 63  
 64     
 65     /**
 66      * 创建签名Sign
 67      * 
 68      * @param key
 69      * @param parameters
 70      * @return
 71      */ 
 72     public static String createSign(SortedMap<String,String> parameters,String key){  
 73         StringBuffer sb = new StringBuffer();  
 74         Set es = parameters.entrySet();
 75         Iterator<?> it = es.iterator();  
 76         while(it.hasNext()) {  
 77             Map.Entry entry = (Map.Entry)it.next();  
 78             String k = (String)entry.getKey();  
 79             if(entry.getValue() != null || !"".equals(entry.getValue())) {
 80                 String v = String.valueOf(entry.getValue());
 81                 if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {
 82                     sb.append(k + "=" + v + "&");
 83                 }
 84             }  
 85         }  
 86         sb.append("key=" + key);  
 87         String sign = MD5Util.MD5Encode(sb.toString(), "UTF-8").toUpperCase();  
 88         return sign;  
 89     }
 90     
 91     
 92     /**
 93      * XML转换为Map
 94      * 
 95      * @param strXML
 96      * @return Map
 97      * @throws Exception
 98      */
 99     public static Map<String, Object> getMapFromXML(String strXML) throws Exception {
100         try {
101             Map<String, Object> data = new HashMap<String, Object>();
102             DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
103             //防止XXE攻击
104             documentBuilderFactory.setXIncludeAware(false);
105             documentBuilderFactory.setExpandEntityReferences(false);
106             DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
107             InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));
108             org.w3c.dom.Document doc = documentBuilder.parse(stream);
109             doc.getDocumentElement().normalize();
110             NodeList nodeList = doc.getDocumentElement().getChildNodes();
111             for (int idx = 0; idx < nodeList.getLength(); ++idx) {
112                 Node node = nodeList.item(idx);
113                 if (node.getNodeType() == Node.ELEMENT_NODE) {
114                     org.w3c.dom.Element element = (org.w3c.dom.Element) node;
115                     data.put(element.getNodeName(), element.getTextContent());
116  
                       
                    
                    

鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
微信小程序-获取音乐链接发布时间:2022-07-18
下一篇:
SpringBoot2.0小程序支付功能实现weixin-java-pay发布时间:2022-07-18
热门推荐
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap