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

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

微信小程序支付

说道小程序支付,我要稍稍吐槽一下,微信支付真的搞的很乱。如果你之前项目中已经接入了微信的扫码和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                 }
117             }
118             try {
119                 stream.close();
120             } catch (Exception ex) {
121                 ex.printStackTrace();
122             }
123             return data;
124         } catch (Exception ex) {
125             throw ex;
126         }
127     }
128     
129     /**
130      * 生成随机数
131      * 
132      * @return
133      */
134     public static String makeUUID(int len) {
135         return UUID.randomUUID().toString().replaceAll("-", "").substring(0, len);
136     }
137     
138     /**
139      * 获取当前的Timestamp
140      * 
141      * @return
142      */
143     public static String getCurrentTimeStamp() {
144         return Long.toString(System.currentTimeMillis()/1000);
145     }
146  
147     /**
148      * 获取当前的时间
149      * @return
150      */
151     public static long getCurrentTimestampMs() {
152         return System.currentTimeMillis();
153     }
154     
155     /**
156      * 生成订单号
157      * 
158      * @return
159      */
160     public static String generateOrderNo() {    
161         SimpleDateFormat sdf  = new SimpleDateFormat("yyMMdd");
162         return sdf.format(new Date())+makeUUID(16);
163     }
164     
165     /**
166      * 获取当前工程url
167      * 
168      * @param request
169      * @return
170      */
171     public static String getCurrentUrl(HttpServletRequest request){
172         return request.getScheme() +"://" + request.getServerName()  + ":" +request.getServerPort() +request.getContextPath();
173     }
174     
175     /**
176      * Xml字符串转换为Map
177      * 
178      * @param xmlStr
179      * @return
180      */
181     public static Map<String,String> xmlStrToMap(String xmlStr){
182         Map<String,String> map = new HashMap<String,String>();
183         Document doc;
184         try {
185             doc = DocumentHelper.parseText(xmlStr);
186             Element root = doc.getRootElement();
187             List children = root.elements();
188             if(children != null && children.size() > 0) {
189                 for(int i = 0; i < children.size(); i++) {
190                     Element child = (Element)children.get(i);
191                     map.put(child.getName(), child.getTextTrim());
192                 }
193             }
194         } catch (DocumentException e) {
195             e.printStackTrace();
196         }
197         return map;
198     }
199     
200     public static String testXml(){
201         return "<xml><appid><![CDATA[wx2421b1c4370ec43b]]></appid><attach><![CDATA[支付测试]]></attach><bank_type><![CDATA[CFT]]></bank_type><fee_type><![CDATA[CNY]]></fee_type> <is_subscribe><![CDATA[Y]]></is_subscribe><mch_id><![CDATA[10000100]]></mch_id><nonce_str><![CDATA[5d2b6c2a8db53831f7eda20af46e531c]]></nonce_str><openid><![CDATA[oUpF8uMEb4qRXf22hE3X68TekukE]]></openid> <out_trade_no><![CDATA[WeChat Pay test]]></out_trade_no><result_code><![CDATA[SUCCESS]]></result_code> <return_code><![CDATA[SUCCESS]]></return_code><sign><![CDATA[B552ED6B279343CB493C5DD0D78AB241]]></sign><sub_mch_id><![CDATA[10000100]]></sub_mch_id> <time_end><![CDATA[20140903131540]]></time_end><total_fee>1</total_fee><trade_type><![CDATA[JSAPI]]></trade_type><transaction_id><![CDATA[1004400740201409030005092168]]></transaction_id></xml>";
202     }
203  
204     
205     public static String getSceneInfo(String wapUrl,String name){
206         Map<String,Map<String,String>> map = new HashMap<String, Map<String,String>>();
207         if(!StringUtils.isEmpty(wapUrl) && !StringUtils.isEmpty(name)){
208             /*{"h5_info": {"type":"Wap","wap_url": "https://pay.qq.com","wap_name": "腾讯充值"}}*/
209             Map<String,String> childmap = new TreeMap<String, String>();
210             childmap.put("type", "Wap");
211             childmap.put("wap_url",wapUrl);
212             childmap.put("wap_name", name);
213             map.put("h5_info", childmap);
214             return JSON.toJSONString(map);
215         }
216         return null;
217     }
218     
219  
220     /**
221      * 转换金额型到整型
222      * @param money
223      * @return
224      */
225     public static String moneyToIntegerStr(Double money){
226         BigDecimal decimal = new BigDecimal(money);
227         int amount = decimal.multiply(new BigDecimal(100))
228             .setScale(0, BigDecimal.ROUND_HALF_UP).intValue();
229         return String.valueOf(amount);
230     }
231  
232     /** 
233      * 除去数组中的空值和签名参数
234      * @param sArray 签名参数组
235      * @return 去掉空值与签名参数后的新签名参数组
236      */
237     public static Map<String, String> paraFilter(Map<String, String> sArray) {
238  
239         Map<String, String> result = new HashMap<String, String>();
240  
241         if (sArray == null || sArray.size() <= 0) {
242             return result;
243         }
244  
245         for (String key : sArray.keySet()) {
246             String value = sArray.get(key);
247             if (value == null || value.equals("") || key.equalsIgnoreCase("sign")
248                 || key.equalsIgnoreCase("sign_type")) {
249                 continue;
250             }
251             result.put(key, value);
252         }
253  
254         return result;
255     }
256     
257     /** 
258      * 把数组所有元素排序,并按照“参数=参数值”的模式用“&”字符拼接成字符串
259      * @param params 需要排序并参与字符拼接的参数组
260      * @return 拼接后字符串
261      */
262     public static String createLinkString(Map<String, String> params) {
263         List<String> keys = new ArrayList<String>(params.keySet());
264         Collections.sort(keys);
265         String prestr = "";
266         for (int i = 0; i < keys.size(); i++) {
267             String key = keys.get(i);
268             String value = params.get(key);
269             if (i == keys.size() - 1) {//拼接时,不包括最后一个&字符
270                 prestr = prestr + key + "=" + value;
271             } else {
272                 prestr = prestr + key + "=" + value + "&";
273             }
274         }
275         return prestr;
276     }
277     
278     /**
279      * 根据不同环境生成支付金额
280      * 
281      * @param env
282      * @param money
283      * @param money
284      * @return
285      */
286     public static double getPayAmountByEnv(String env, Double money){
287         double pay_money = 0.01;
288         //测试环境
289         if("dev".equals(env)){
290             return 0.01;
291         }else{
292             //生成环境
293             return money;
294         }
295     }
296 }

8,Controller接口编写

@RestController
@RequestMapping(value = "/api/payment/")
public class PaymentController {
 
    private static Logger logger = LoggerFactory.getLogger(PaymentController.class);
 
    @Value("${server.domain}")
    private String serverDomain;
    @Autowired
    private PaymentService paymentService;
 
    /**
     * <p>微信小程序支付接口</p>
     * 必传参数:openId, orderNo, payAmount
     *
     * @param request
     * @param paymentBo
     * @return
     * @throws Exception
     */
    @ResponseBody
    @RequestMapping(value="ltePay", method=RequestMethod.POST, produces = {"application/json;charset=UTF-8"})
    public ResultData toPay(HttpServletRequest request, @RequestBody PaymentBo paymentBo) throws Exception {
        if (paymentBo == null || paymentBo.getOpenId() == null || paymentBo.getOrderNo() == null || paymentBo.getPayAmount() == null) {
            throw new ParamException();
        } else if (paymentBo.getPayAmount() < 0.01) {
            return ResultData.fail("订单金额有误,请确认!");
        }else{
            logger.info("【小程序支付服务】请求订单编号: ["+paymentBo.getOrderNo()+"]");
            Map<String, String> resMap = paymentService.wxLtePayment(paymentBo.getOpenId(), paymentBo.getOrderNo(), paymentBo.getPayAmount());
            if("SUCCESS".equals(resMap.get("returnCode")) && "OK".equals(resMap.get("returnMsg"))){
                //统一下单成功
                resMap.remove("returnCode");
                resMap.remove("returnMsg");
                logger.info("【小程序支付服务】支付下单成功!");
                return ResultData.ok(resMap);
            }else{
                logger.info("【小程序支付服务】支付下单失败!原因:"+resMap.get("returnMsg"));
                return ResultData.fail(resMap.get("returnMsg"));
            }
        }
    }
    
    /**
     * 微信支付完成回调Api
     * 
     * @param request
     * @param response
     * @throws Exception
     */
    @RequestMapping(value="wxNotify")
    public void wxNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {
         InputStream inputStream =  request.getInputStream();
         //获取请求输入流
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len = 0;
        while ((len=inputStream.read(buffer))!=-1){
            outputStream.write(buffer,0,len);
        }
        outputStream.close();
        inputStream.close();
        Map<String,Object> map = BeanToMap.getMapFromXML(new String(outputStream.toByteArray(),"utf-8"));
        logger.info("【微信支付回调】 回调数据: \n"+map);
        String resXml = "";
        String returnCode = (String) map.get("return_code");
        if ("SUCCESS".equalsIgnoreCase(returnCode)) {
            String returnmsg = (String) map.get("result_code");
            if("SUCCESS".equals(returnmsg)){
                //支付成功
                resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
                         + "<return_msg><![CDATA[OK]]></return_msg>"+"</xml>";
            }else{
                resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
                        + "<return_msg><![CDATA[报文为空]></return_msg>" + "</xml>";
                logger.info("支付失败:"+resXml);
            }
        }else{
            resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
                    + "<return_msg><![CDATA[报文为空]></return_msg>" + "</xml>";
            logger.info("【订单支付失败】");
        }
        
        logger.info("【微信支付回调响应】 响应内容:\n"+resXml);
        //做出响应
        response.getWriter().print(resXml);
    }
    
    /**
     * <p>查询订单支付是否完成</p>
     *
     * @param orderNo
     * @return
     * @throws Exception
     */
    @ResponseBody
    @GetMapping(value="queryPayStatus")
    public ResultData queryPayStatus(String orderNo) throws Exception {
        if (StringUtils.isEmpty(orderNo)) {
            throw new ParamException();
        }
        PaymentRecord record = paymentService.queryPaymentStatusById(orderNo);
        if(record != null){
            Map<String,Object> map = new HashMap<String,Object>();
            map.put("companyName", PaymentConstants.COMPANY_NAME);
            map.put("OrderNo", record.getOrderNo());
            map.put("payAmount",record.getPayAmont());
            map.put("payMethod",PaymentConstants.getPayMethod(String.valueOf(record.getPayMethod())));
            map.put("tradeNo",record.getTradeNo());
            map.put("payTime",record.getUpdateTime());
            map.put("payStatus",record.getStatus());
            return ResultData.ok(map);
        }else{
            return ResultData.fail("未支付!");
        }
    }
    
}

9,Controller接收参数Bo

public class PaymentBo {
 
    /**支付类型:1-支付宝;2-微信*/
    private Integer payType;
    /**客户openId*/
    private String openId;
    /**订单编号*/
    private String orderNo;
    /**支付金额*/
    private Double payAmount;
 
    public Integer getPayType() {
        return payType;
    }
 
    public String getOpenId() {
        return openId;
    }
 
    public String getOrderNo() {
        return orderNo;
    }
 
    public Double getPayAmount() {
        return payAmount;
    }
 
    public void setPayType(Integer payType) {
        this.payType = payType;
    }
 
    public void setOpenId(String openId) {
        this.openId = openId;
    }
 
    public void setOrderNo(String orderNo) {
        this.orderNo = orderNo;
    }
 
    public void setPayAmount(Double payAmount) {
        this.payAmount = payAmount;
    }
}

10,Controller接口返回类封装:

 1 public class ResultData<T> implements Serializable {
 2 
 3   private static final long serialVersionUID = 1L;
 4 
 5   private Integer code;
 6   private String message;
 7   private T data;
 8 
 9   public Integer getCode() {
10     return code;
11   }
12   public String getMessage() {
13     return message;
14   }
15   public T getData() {
16     return data;
17   }
18   public void setCode(Integer code) {
19     this.code = code;
20   }
21   public void setMessage(String message) {
22     this.message = message;
23   }
24   public void setData(T data) {
25     this.data = data;
26   }
27 
28   public ResultData() {}
29 
30   public ResultData(ResultCodeEnums status, T data) {
31     this(status.getCode(), status.getMessage(), data);
32   }
33 
34   public ResultData(Integer code, String message, T data) {
35     this.code = code;
36     this.message = message;
37     this.data = data;
38   }
39 
40   public static <T> ResultData<T> ok(String message){
41     return new ResultData<T>(ResultCodeEnums.SUCCESS, null);
42   }
43 
44   public static <T> ResultData<T> ok(T data){
45     return new ResultData<T>(ResultCodeEnums.SUCCESS,data);
46   }
47 
48   public static <T> ResultData<T> ok(String message,T data){
49     return new ResultData<T>(ResultCodeEnums.SUCCESS.getCode(), message, data);
50   }
51 
52   public static <T> ResultData<T> fail(String message){
53     return fail(null);
54   }
55 
56   public static <T> ResultData<T> fail(T data){
57     return new ResultData<T>(ResultCodeEnums.FAIL, data);
58   }
59 
60   public static <T> ResultData<T> fail(String message,T data){
61     return new ResultData<T>(ResultCodeEnums.FAIL.getCode(), message, data);
62   }
63 }

二,小程序端(获取统一下单返回参数发起支付)

在小程序端,发起支付请求到,Java后台的统一下单接口返回prepay_id等参数,然后封装调起微信的js方法:wx.requestPayment(OBJECT),具体参考文档:官方文档

测试一把:

 

本篇代码是我们运行两年电商项目的源码,目前做的已经很完整健壮,由于依赖和工具类等很多,不能全部贴出来,还请谅解。如果需要,请扫描添加公众号,联系本人获取其他代码或支付项目。

posted @ 2020-04-23 16:23  码农大哥  阅读(842)  评论(0编辑  收藏  举报