注冊登錄

微信支付服務(wù)商接入小程序指引步驟流程

2018-09-20
導(dǎo)讀:微信支付服務(wù)商接入指引 本文主要針對服務(wù)商下特約商戶的小程序支付進(jìn)行講解。(掃碼支付, h5支付大致流程都差不多,了解了小程序支付能夠很快接入其他支付類型) 說明:本文...

微信支付服務(wù)商接入指引

本文主要針對服務(wù)商下特約商戶的小程序支付進(jìn)行講解。(掃碼支付, h5支付大致流程都差不多,了解了小程序支付能夠很快接入其他支付類型)

說明:本文中的支付都是指在服務(wù)商模式下

支付主體

  • 服務(wù)商:擁有支付開發(fā)能力的第三方提供商
  • 普通商戶: 擁有開發(fā)能力的商戶
  • 特約商戶:服務(wù)商下的商戶

    一個商家主體可以在不同服務(wù)商下申請?zhí)丶s商戶,每個服務(wù)商都會給商家主體在此服務(wù)商下一個特約商戶號。

    普通商戶申請需要花費(fèi)大約300RMB,服務(wù)商申請?zhí)丶s商戶不需要費(fèi)用。

    一個商家主體可以申請 普通商戶,特約商戶。同一個商戶主體申請的普通商戶與在服務(wù)商下申請的特約商戶號是獨(dú)立的。

    服務(wù)商

    服務(wù)商下的特約商戶的資金流轉(zhuǎn)不會直接經(jīng)過服務(wù)商的支付賬戶,最終消費(fèi)者的資金直接和服務(wù)商下的特約商戶進(jìn)行來往,但是服務(wù)商可以查看自己下的特約商戶資金流水。

    服務(wù)商小程序開發(fā)文檔

    開發(fā)支付

    開發(fā)之前

    申請注冊服務(wù)商,通過之后登錄微信商戶平臺,進(jìn)入菜單: 服務(wù)商功能 --> 特約商戶管理 -->新增商戶(也就是申請服務(wù)商下的特約商戶)
    申請如果沒有問題會在三到五天通過,之后可以在特約商戶管理
    下看到服務(wù)商自己的特約商戶,我們在開發(fā)中需要服務(wù)商商戶號及這里的商戶號(特約商戶號)

    支付需要接口:微信統(tǒng)一下單,及提供給微信的回調(diào)接口

    統(tǒng)一下單接口

    微信統(tǒng)一下單請求參數(shù)

    統(tǒng)一下單請求參數(shù)封裝為我們可以處理的對象:

    此處我的命名是: WechatUnifiedorderRequest

    以下是我開發(fā)中遇到一些坑,主要是由于微信官方的文檔給的參數(shù)很模糊,特別是小程序支付。

    在填充好了WechatUnifiedorderRequest對象后對我們填充的值按照字典排序,連接key進(jìn)行簽名,以xml格式字符向微信發(fā)起請求

    1. 我們需要對對象按照字典序排序

      第一步,設(shè)所有發(fā)送或者接收到的數(shù)據(jù)為集合M,將集合M內(nèi)非空參數(shù)值的參數(shù)按照參數(shù)名ASCII碼從小到大排序(字典序),使用URL鍵值對的格式(即key1=value1&key2=value2…)拼接成字符串stringA。
    2. 字典排序后的字符連接key(需要在微信商戶平臺進(jìn)行配置建議使用UUID生成32位)
    3. MD5加密簽名,得到sign填充WechatUnifiedorderRequest對象
    4. WechatUnifiedorderRequest轉(zhuǎn)換為微信需要的xml類型
    5. 發(fā)起請求
    6. 得到微信統(tǒng)一下單的響應(yīng)(是xml字符格式),解析為對象(對返回的響應(yīng)封裝對象進(jìn)行處理WechatUnifiedorderResponse),
    7. 對返回的對象進(jìn)行驗(yàn)證,通過驗(yàn)證返回給小程序 需要的參數(shù)及簽名小程序調(diào)起支付API
    8. 小程序支付成功,微信開始回調(diào)在統(tǒng)一下單傳給微信的回調(diào)地址
      獲取下單用戶的真實(shí)IP
       /**
           * 獲取用戶真實(shí)IP
           * 如果有代理,獲取真實(shí)客戶端IP
           * @param request
           * @return
           */
          public  static  String getRealId(HttpServletRequest request){
      
              String xForwardedForHeader= request.getHeader("X-Forwarded-For");
              if(xForwardedForHeader == null){
                  return  request.getRemoteAddr();
      
              }else {
                  return  new StringTokenizer(xForwardedForHeader, ",").nextToken().trim();
              }
      
          }
      按照字典序排序
       /**
           * 使用java反射機(jī)制,動態(tài)獲取對象的屬性和參數(shù)值,排除值為null的情況,并按字典序排序
           * @param object
           * @return
           */
          public static   String getSortMap(Object object) throws  Exception{
              //1.得到屬性的名稱及值 如果為null不存入map
              Field [] fields = object.getClass().getDeclaredFields();
              Map map = new HashMap<>();
              for(Field field : fields){
                  String name = field.getName();
                  /*String methodName = "get"+name.replaceFirst(name.substring(0, 1), name.substring(0, 1)
                          .toUpperCase());*/
                  //通過get方法直接獲取屬性值
                  field.setAccessible(true);
                  Object value = field.get(object);
                  if (value != null){
                      map.put(name, value.toString());
                  }
      
      
              }
              //排序
              Map sortMap = new TreeMap(
                      new Comparator() {
      
                          @Override
                          public int compare(String arg0, String arg1) {
      
                              return arg0.compareTo(arg1);
                          }
                      });
              sortMap.putAll(map);
      
      
              StringBuilder sortFeil = new StringBuilder();
              //得到鍵值對的格式(即key1=value1&key2=value2…
              sortMap.forEach((k,v)-> {
                  sortFeil.append(k+"="+v+"&");
              });
              //移除最后一個 &
              sortFeil.deleteCharAt(sortFeil.length()-1);
              return sortFeil.toString();
      
          }
      

      使用字典序返回的字符連接key,使用MD5進(jìn)行加密,得到sign

      WechatUnifiedorderRequest轉(zhuǎn)換為微信需要的xml類型

      在WechatUnifiedorderRequest對象上使用注解

      • @xmlAccessorType@xmlAccessorType(XmlAccessType.FIELD)
      • @xmlRootElement@xmlRootElement(name ="xml") ( name = "xml : "WechatUnifiedorderReques對象轉(zhuǎn)換為xml的根名稱)
        /**
         * 微信統(tǒng)一下單請求對象
         *
         * @Author xuelongjiang
         */
        @XmlAccessorType(XmlAccessType.FIELD)
        @XmlRootElement(name = "xml")//xml的根元素
        public class WechatUnifiedorderRequest  implements Serializable{
        }
        

        對象轉(zhuǎn)換為xml字符
        引入包:import javax.xml.bind.JAXBContext

         /**
             * 對象轉(zhuǎn)換為xml
             * @param object
             * @return
             */
            public static  String objectToXml(Object object){
        
                StringWriter sw = new StringWriter();
                try {
        
                    JAXBContext context = JAXBContext.newInstance(object.getClass());
                    Marshaller marshaller =  context.createMarshaller();
                    marshaller.marshal(object,sw);
        
                }catch (Exception e){
                    e.printStackTrace();
                    logger.error("對象解析xml出現(xiàn)異常,對象為"+object.toString());
                }
        
                return sw.toString();
            }
        得到微信統(tǒng)一下單的響應(yīng)(是xml字符格式),解析為對象

        封裝對象:WechatUnifiedorderResponse 表示微信統(tǒng)一下單響應(yīng)的對象。

        請求微信統(tǒng)一下單返回示例:

        
           
           
           
           
           
           ![CDATA[10000101]]>
           
           
           
           
           
        

        參數(shù)值用XML轉(zhuǎn)義即可,CDATA標(biāo)簽用于說明數(shù)據(jù)不被XML解析器解析,在轉(zhuǎn)為對象的時候我們需要解析

        WechatUnifiedorderResponse對象使用注解

        • @XmlAccessorType(XmlAccessType.FIELD)
          • @XmlRootElement(name = "xml")//解析xml的根元素

            以上的和WechatUnifiedorderRequest是一樣,但是由于需要解析,我們創(chuàng)建CDataAdapter繼承XmlAdapter ,使用注解@XmlJavaTypeAdapter來處理,在WechatUnifiedorderResponse需要處理的域上使用注解

            如下:

             @XmlJavaTypeAdapter(CDataAdapter.class)// 解析
                private String return_code; //返回狀態(tài)碼
            CDataAdapter解析
            
            /**
             *
             * 注解使用, 對象與xml轉(zhuǎn)換的字段需要有 
             *
             * @Author xuelongjiang
             */
            public class CDataAdapter extends XmlAdapter {
            
                private static Logger logger = LoggerFactory.getLogger(CDataAdapter.class);
            
                /**
                 * Do-nothing constructor for the derived classes.
                 */
                protected CDataAdapter() {
                    super();
                }
            
                /**
                 * Convert a value type to a bound type.
                 *
                 * @param v The value to be converted. Can be null.
                 * @throws Exception if there's an error during the conversion. The caller is responsible for
                 *                   reporting the error to the user through {@link ValidationEventHandler}.
                 */
                @Override
                public String unmarshal(String v) throws Exception {
            
                  if("".equals(v)){
                      return "";
                  }
                  String v1 = null;
                  String v2 = null;
            
                  String subStart = "= 0){
                      v1 = v.substring(subStart.length(),v.length());
            
                  }else {
                      return v;
                  }
                  String subEnd = "]]>";
                  int b = v1.indexOf(subEnd);
                  if(b>= 0){
                      v2 = v1.substring(0,b);
                  }
                  return v2;
            
                }
            
                /**
                 * Convert a bound type to a value type.
                 *
                 * @param v The value to be convereted. Can be null.
                 * @throws Exception if there's an error during the conversion. The caller is responsible for
                 *                   reporting the error to the user through {@link ValidationEventHandler}.
                 */
                @Override
                public String marshal(String v) throws Exception {
            
                    logger.info("對象轉(zhuǎn)換xml:"+"");
                    return "";
                }
            }
            
            

            到此為止,我們已經(jīng)得到微信統(tǒng)一下單的響應(yīng)值了,后續(xù)的處理不是很復(fù)雜。按照文檔不會有很大的坑。

            在做微信支付的時候,難點(diǎn)是以上的:請求參數(shù)說明模糊,在經(jīng)歷幾次的傳參試驗(yàn)及百度谷歌之后,才明白了參數(shù)的具體的使用,其實(shí)后續(xù)在做掃碼支付的時候,發(fā)現(xiàn)掃碼支付解釋的比較清楚,小程序的文檔確實(shí)比較坑。

重磅推薦:小程序開店目錄

第一部分:小商店是什么

第二部分:如何開通一個小商店

第三部分:如何登錄小商店

第四部分:開店任務(wù)常見問題

第五部分:小商店可以賣什么

第六部分:HiShop小程序特色功能

第七部分:小程序直播

第八部分:小程序收貨/物流

第九部分:小程序怎么結(jié)算

第十部分:小程序客服

第十一部分:電商創(chuàng)業(yè)

第十二部分:小程序游戲開發(fā)

電話咨詢 微信咨詢 預(yù)約演示 0元開店