zoukankan      html  css  js  c++  java
  • 微信小程序支付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("【微信支付】 统一下单响应:
    "+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("【微信支付回调】 回调数据: 
    "+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("【微信支付回调响应】 响应内容:
    "+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),具体参考文档:官方文档

    测试一把:

     

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

  • 相关阅读:
    ==和===
    Println、Printf、Sprintf区别
    BurpSuite代理https
    scp
    Tomcat Ajp(CVE-2020-1938)
    Chrome-HackBar破解
    crontab
    Sql注入之postgresql
    Sql注入之oracle
    LeetCode简单题(一)
  • 原文地址:https://www.cnblogs.com/lyn20141231/p/12761675.html
Copyright © 2011-2022 走看看