微信支付服務(wù)商接入小程序指引步驟流程
微信支付服務(wù)商接入指引
本文主要針對(duì)服務(wù)商下特約商戶的小程序支付進(jìn)行講解。(掃碼支付, h5支付大致流程都差不多,了解了小程序支付能夠很快接入其他支付類型)
說明:本文中的支付都是指在服務(wù)商模式下
支付主體
- 服務(wù)商:擁有支付開發(fā)能力的第三方提供商
- 普通商戶: 擁有開發(fā)能力的商戶
-
特約商戶:服務(wù)商下的商戶
一個(gè)商家主體可以在不同服務(wù)商下申請(qǐng)?zhí)丶s商戶,每個(gè)服務(wù)商都會(huì)給商家主體在此服務(wù)商下一個(gè)特約商戶號(hào)。
普通商戶申請(qǐng)需要花費(fèi)大約300RMB,服務(wù)商申請(qǐng)?zhí)丶s商戶不需要費(fèi)用。
一個(gè)商家主體可以申請(qǐng) 普通商戶,特約商戶。同一個(gè)商戶主體申請(qǐng)的普通商戶與在服務(wù)商下申請(qǐng)的特約商戶號(hào)是獨(dú)立的。
服務(wù)商
服務(wù)商下的特約商戶的資金流轉(zhuǎn)不會(huì)直接經(jīng)過服務(wù)商的支付賬戶,最終消費(fèi)者的資金直接和服務(wù)商下的特約商戶進(jìn)行來往,但是服務(wù)商可以查看自己下的特約商戶資金流水。
服務(wù)商小程序開發(fā)文檔
開發(fā)支付
開發(fā)之前
申請(qǐng)注冊(cè)服務(wù)商,通過之后登錄微信商戶平臺(tái),進(jìn)入菜單: 服務(wù)商功能 --> 特約商戶管理 -->新增商戶(也就是申請(qǐng)服務(wù)商下的特約商戶)
申請(qǐng)如果沒有問題會(huì)在三到五天通過,之后可以在特約商戶管理
下看到服務(wù)商自己的特約商戶,我們?cè)陂_發(fā)中需要服務(wù)商商戶號(hào)及這里的商戶號(hào)(特約商戶號(hào))支付需要接口:微信統(tǒng)一下單,及提供給微信的回調(diào)接口
統(tǒng)一下單接口
微信統(tǒng)一下單請(qǐng)求參數(shù)
統(tǒng)一下單請(qǐng)求參數(shù)封裝為我們可以處理的對(duì)象:
此處我的命名是: WechatUnifiedorderRequest
以下是我開發(fā)中遇到一些坑,主要是由于微信官方的文檔給的參數(shù)很模糊,特別是小程序支付。
在填充好了WechatUnifiedorderRequest對(duì)象后對(duì)我們填充的值按照字典排序,連接key進(jìn)行簽名,以xml格式字符向微信發(fā)起請(qǐng)求
-
我們需要對(duì)對(duì)象按照字典序排序
第一步,設(shè)所有發(fā)送或者接收到的數(shù)據(jù)為集合M,將集合M內(nèi)非空參數(shù)值的參數(shù)按照參數(shù)名ASCII碼從小到大排序(字典序),使用URL鍵值對(duì)的格式(即key1=value1&key2=value2…)拼接成字符串stringA。 - 字典排序后的字符連接key(需要在微信商戶平臺(tái)進(jìn)行配置建議使用UUID生成32位)
- MD5加密簽名,得到sign填充WechatUnifiedorderRequest對(duì)象
- WechatUnifiedorderRequest轉(zhuǎn)換為微信需要的xml類型
- 發(fā)起請(qǐng)求
- 得到微信統(tǒng)一下單的響應(yīng)(是xml字符格式),解析為對(duì)象(對(duì)返回的響應(yīng)封裝對(duì)象進(jìn)行處理WechatUnifiedorderResponse),
- 對(duì)返回的對(duì)象進(jìn)行驗(yàn)證,通過驗(yàn)證返回給小程序 需要的參數(shù)及簽名小程序調(diào)起支付API
-
小程序支付成功,微信開始回調(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ī)制,動(dòng)態(tài)獲取對(duì)象的屬性和參數(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(); //得到鍵值對(duì)的格式(即key1=value1&key2=value2… sortMap.forEach((k,v)-> { sortFeil.append(k+"="+v+"&"); }); //移除最后一個(gè) & sortFeil.deleteCharAt(sortFeil.length()-1); return sortFeil.toString(); } 使用字典序返回的字符連接key,使用MD5進(jìn)行加密,得到sign
WechatUnifiedorderRequest轉(zhuǎn)換為微信需要的xml類型
在WechatUnifiedorderRequest對(duì)象上使用注解
- @xmlAccessorType@xmlAccessorType(XmlAccessType.FIELD)
-
@xmlRootElement@xmlRootElement(name ="xml") ( name = "xml : "WechatUnifiedorderReques對(duì)象轉(zhuǎn)換為xml的根名稱)
/** * 微信統(tǒng)一下單請(qǐng)求對(duì)象 * * @Author xuelongjiang */ @XmlAccessorType(XmlAccessType.FIELD) @XmlRootElement(name = "xml")//xml的根元素 public class WechatUnifiedorderRequest implements Serializable{ }
對(duì)象轉(zhuǎn)換為xml字符
引入包:import javax.xml.bind.JAXBContext/** * 對(duì)象轉(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("對(duì)象解析xml出現(xiàn)異常,對(duì)象為"+object.toString()); } return sw.toString(); }
得到微信統(tǒng)一下單的響應(yīng)(是xml字符格式),解析為對(duì)象
封裝對(duì)象:WechatUnifiedorderResponse 表示微信統(tǒng)一下單響應(yīng)的對(duì)象。
請(qǐng)求微信統(tǒng)一下單返回示例:
![CDATA[10000101]]> 參數(shù)值用XML轉(zhuǎn)義即可,CDATA標(biāo)簽用于說明數(shù)據(jù)不被XML解析器解析,在轉(zhuǎn)為對(duì)象的時(shí)候我們需要解析
WechatUnifiedorderResponse對(duì)象使用注解
-
-
@XmlAccessorType(XmlAccessType.FIELD)
-
@XmlRootElement(name = "xml")//解析xml的根元素
以上的和WechatUnifiedorderRequest是一樣,但是由于需要解析,我們創(chuàng)建CDataAdapter繼承XmlAdapter ,使用注解@XmlJavaTypeAdapter來處理,在WechatUnifiedorderResponse需要處理的域上使用注解
如下:
@XmlJavaTypeAdapter(CDataAdapter.class)// 解析 private String return_code; //返回狀態(tài)碼
CDataAdapter解析
/** * * 注解使用, 對(duì)象與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("對(duì)象轉(zhuǎn)換xml:"+""); return ""; } } 到此為止,我們已經(jīng)得到微信統(tǒng)一下單的響應(yīng)值了,后續(xù)的處理不是很復(fù)雜。按照文檔不會(huì)有很大的坑。
在做微信支付的時(shí)候,難點(diǎn)是以上的:請(qǐng)求參數(shù)說明模糊,在經(jīng)歷幾次的傳參試驗(yàn)及百度谷歌之后,才明白了參數(shù)的具體的使用,其實(shí)后續(xù)在做掃碼支付的時(shí)候,發(fā)現(xiàn)掃碼支付解釋的比較清楚,小程序的文檔確實(shí)比較坑。
-
@XmlRootElement(name = "xml")//解析xml的根元素
-
@XmlAccessorType(XmlAccessType.FIELD)
-
第二部分:如何開通一個(gè)小商店