#微信支付服务商接入指引

本文主要针对服务商下特约商户的小程序支付进行讲解。(扫码支付, h5支付大致流程都差不多,了解了小程序支付能够很快接入其他支付类型)

说明:本文中的支付都是指在服务商模式下

##支付主体

  • 服务商:拥有支付开发能力的第三方提供商
  • 普通商户: 拥有开发能力的商户
  • 特约商户:服务商下的商户

一个商家主体可以在不同服务商下申请特约商户,每个服务商都会给商家主体在此服务商下一个特约商户号。

普通商户申请需要花费大约300RMB,服务商申请特约商户不需要费用。

一个商家主体可以申请 普通商户,特约商户。同一个商户主体申请的普通商户与在服务商下申请的特约商户号是独立的。

##服务商

服务商下的特约商户的资金流转不会直接经过服务商的支付账户,最终消费者的资金直接和服务商下的特约商户进行来往,但是服务商可以查看自己下的特约商户资金流水。

服务商小程序开发文档

##开发支付
###开发之前
申请注册服务商,通过之后登录微信商户平台,进入菜单: 服务商功能 --> 特约商户管理 -->新增商户(也就是申请服务商下的特约商户)
申请如果没有问题会在三到五天通过,之后可以在特约商户管理
下看到服务商自己的特约商户,我们在开发中需要 服务商商户号及这里的商户号(特约商户号)
###支付需要接口:微信统一下单,及提供给微信的回调接口

微信官方给的业务流程图:

可以很清晰的理解业务流程走向。
###统一下单接口

微信统一下单请求参数

统一下单请求参数封装为我们可以处理的对象:

此处我的命名是: WechatUnifiedorderRequest

以下是我开发中遇到一些坑,主要是由于微信官方的文档给的参数很模糊,特别是小程序支付。

名称 描述
appid 公众号appid
mch_id 服务商号
sub_appid 小程序的appId
notify-url 微信回调地址
trade_type 交易类型 JSAPI:小程序 NATIVE:扫码支付
nonce_str 32位随机字符
sub_mch_id 特约商户号
totalPrice 支付金额单位分
spbill_create_ip 发起支付者的IP
sub_openid 发起支付的微信统一标识

####对我们填充的值按照字典排序,连接key进行签名,以xml格式字符向微信发起请求

在填充好了WechatUnifiedorderRequest对象后

  1. 我们需要对对象按照字典序排序

    第一步,设所有发送或者接收到的数据为集合M,将集合M内非空参数值的参数按照参数名ASCII码从小到大排序(字典序),使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串stringA。

  2. 字典排序后的字符连接key(需要在微信商户平台进行配置建议使用UUID生成32位)

  3. MD5加密签名,得到sign填充WechatUnifiedorderRequest对象

  4. WechatUnifiedorderRequest转换为微信需要的xml类型

  5. 发起请求

  6. 得到微信统一下单的响应(是xml字符格式),解析为对象(对返回的响应封装对象进行处理WechatUnifiedorderResponse),

  7. 对返回的对象进行验证,通过验证返回给小程序 需要的参数及签名 小程序调起支付API

  8. 小程序支付成功,微信开始回调在统一下单传给微信的回调地址

#####获取下单用户的真实IP

 /*** 获取用户真实IP* 如果有代理,获取真实客户端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反射机制,动态获取对象的属性和参数值,排除值为null的情况,并按字典序排序* @param object* @return*/public static   String getSortMap(Object object) throws  Exception{//1.得到属性的名称及值 如果为null不存入mapField [] fields = object.getClass().getDeclaredFields();Map<String,String> 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<String, String> sortMap = new TreeMap<String,String>(new Comparator<String>() {@Overridepublic 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进行加密,得到sign

#####WechatUnifiedorderRequest转换为微信需要的xml类型

在WechatUnifiedorderRequest对象上使用注解

  • @xmlAccessorType @xmlAccessorType(XmlAccessType.FIELD)
  • @xmlRootElement @xmlRootElement(name =“xml”) ( name = "xml : "WechatUnifiedorderReques对象转换为xml的根名称)
/*** 微信统一下单请求对象** @Author xuelongjiang*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "xml")//xml的根元素
public class WechatUnifiedorderRequest  implements Serializable{}

对象转换为xml字符
引入包:import javax.xml.bind.JAXBContext

 /*** 对象转换为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出现异常,对象为"+object.toString());}return sw.toString();}
得到微信统一下单的响应(是xml字符格式),解析为对象

封装对象:WechatUnifiedorderResponse 表示微信统一下单响应的对象。

请求微信统一下单返回示例:

<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg><appid><![CDATA[wx2421b1c4370ec43b]]></appid><sub_appid><![CDATA[wx2421b1c4370ec11b]]></sub_appid><mch_id><![CDATA[10000100]]></mch_id><sub_mch_id>![CDATA[10000101]]></appid><nonce_str><![CDATA[IITRi8Iabbblz1Jc]]></nonce_str><sign><![CDATA[7921E432F65EB8ED0CE9755F0E86D72F]]></sign><result_code><![CDATA[SUCCESS]]></result_code><prepay_id><![CDATA[wx201411101639507cbf6ffd8b0779950874]]></prepay_id><trade_type><![CDATA[JSAPI]]></trade_type>
</xml>

参数值用XML转义即可,CDATA标签用于说明数据不被XML解析器解析,在转为对象的时候我们需要解析
<![CDATA[]]>

WechatUnifiedorderResponse对象使用注解

  • @XmlAccessorType(XmlAccessType.FIELD)
  • @XmlRootElement(name = “xml”)//解析xml的根元素
    以上的和WechatUnifiedorderRequest是一样,但是由于需要解析**<![CDATA[]]>,我们创建CDataAdapter继承XmlAdapter ,使用注解@XmlJavaTypeAdapter来处理,在WechatUnifiedorderResponse需要处理<![CDATA[]]>**的域上使用注解

如下:

 @XmlJavaTypeAdapter(CDataAdapter.class)// 解析<![CDATA[]]>private String return_code; //返回状态码

######CDataAdapter解析<![CDATA[]]>


/**** 注解使用, 对象与xml转换的字段需要有 <![CDATA[]]>** @Author xuelongjiang*/
public class CDataAdapter extends XmlAdapter<String,String> {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}.*/@Overridepublic String unmarshal(String v) throws Exception {if("<![CDATA[]]>".equals(v)){return "";}String v1 = null;String v2 = null;String subStart = "<![CDATA[";int a = v.indexOf(subStart);if(a>= 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}.*/@Overridepublic String marshal(String v) throws Exception {logger.info("对象转换xml:"+"<![CDATA["+ v +"]]>");return "<![CDATA["+ v +"]]>";}
}

到此为止,我们已经得到微信统一下单的响应值了,后续的处理不是很复杂。按照文档不会有很大的坑。

在做微信支付的时候,难点是以上的:请求参数说明模糊,在经历几次的传参试验及百度谷歌之后,才明白了参数的具体的使用,其实后续在做扫码支付的时候,发现扫码支付解释的比较清楚,小程序的文档确实比较坑。

参考文档:

https://developers.weixin.qq.com/blogdetail?action=get_post_info&lang=zh_CN&token=&docid=0f0110d772c9d219296b54d4665b7001

https://segmentfault.com/a/1190000009346755#articleHeader2

https://developers.weixin.qq.com/blogdetail?action=get_post_info&lang=zh_CN&token=&docid=0f0110d772c9d219296b54d4665b7001

微信支付服务商接入指引相关推荐

  1. 在线支付系列【21】微信支付服务商接入前准备

    有道无术,术尚可求,有术无道,止于术. 文章目录 项目概述 接入准备 1. 注册服务商号(获取服务商mchid) 2. 注册公众号(获取服务商APPID) 3. 绑定应用ID和服务商ID 4. 入驻子 ...

  2. 微信支付服务商,可视化进件特约商户

    服务商拓展特约商户(子商户),可能出现如下问题: 1.人工录入大量商户资料,耗时耗力. 2.商户对标准费率不满意,无法说服商户先签约再帮其调整费率. 针对以上问题,微信支付面向服务商开放"特 ...

  3. 在线支付系列【20】微信支付服务商介绍

    有道无术,术尚可求,有术无道,止于术. 文章目录 概述 三大服务 支付服务 经营服务 硬件服务 服务商类型 普通微信支付服务商 从业机构(银行) 从业机构(支付机构) 接入指引 特约商户进件 基础支付 ...

  4. 微信支付服务商模式——前期准备

    目录 前言 微信支付的四种模式 成为服务商 登录服务商 商户入驻 从页面入驻 调用API方式入驻 重要的参数 APPID mchid sub_mchid APIv3 key Private key M ...

  5. 微信支付服务商,消费者投诉处理系统

    有读者朋友需要接入微信支付消费者投诉功能,安排. 为了让商户.服务商可以快速获取消费者投诉并进行处理,提高解决消费者投诉的处理能力及效率,为用户提供更优质的服务体验,微信特此提供该消费者投诉API产品 ...

  6. 微信支付服务商模式开发流程

    背景:已完成微信支付商户模式的开发,微信支付需要转换为服务商形式 微信服务商的子商户是特约商户,这个特约商户需要由服务商账号在微信服务商平台进行重新申请,不能将旧的商户直接绑定到服务商下面.后续服务商 ...

  7. 微信支付商户接入(一)

    微信支付商户接入(一) JSAPI支付 沙盒环境测试 常见错误说明: 个人遇见的几个错误及解决办法: 使用微信支付的过程中,踩了不少坑,这里记录下遇到的问题,方便后续的开发者查找和解决问题. JSAP ...

  8. 微信支付服务商,![CDATA[sub_mch_id与sub_appid不匹配],微信小程序支付,签名错误,CDATA[签名错误]解决方法,支付签名验证失败

    微信支付服务商,微信小程序支付,签名错误,CDATA[签名错误]解决方法 1:服务商和普通的商户支付在代码上没有太大差异(不同的是上传的参数,而且微信支付服务商参数上比较难找,官方文档又太简洁,所以很 ...

  9. 前端微信签名验证工具_微信支付服务商签名验证无误,但是统一下单后服务器返回【签名错误】...

    微信支付服务商签名验证无误,但是统一下单后服务器返回, 同样的代码,采用普通商户模式,一切正常,采用服务商受理模式就签名错误,很是无奈. 普通商务模式,统一下单提交数据如下,可以正常支付: wx4a7 ...

  10. 微信支付SDK接入流程梳理

    微信SDK的支付功能接入简单梳理. 首先说一下,你需要的官网都有,但是官网提供的东西不管新旧与否先给你放上去,部分地方提供的链接点击时还提示404,不同的页面提示相同的下载内容(demo)还不一样,表 ...

最新文章

  1. mongodb最多数据库_数据库库库库库库库库!
  2. Python–新一代编程语言
  3. android 消除标题,Android Activity 去掉标题栏及全屏显示
  4. 北理工计算机学院新闻,北理工计算机学子勇夺CVPR 2021 AI安全国际顶级赛事季军...
  5. JS设计模式五:职责链模式
  6. 网易 html5,别再想不开做H5了
  7. 程序员的噩梦:世界上最难的5种编程语言
  8. webpack代码分割和懒加载
  9. 苹果首席芯片设计师离职 曾领导自A7以来所有芯片设计
  10. python 暂停程序 等待用户输入_Python-基础02-程序与用户交互
  11. 2016年最新C/C++学习路线图 附完整视频资源
  12. Java入门的程序汇总
  13. WINDOWS PHONE死于自己不兼容
  14. c语言如何框出视频或图像中的车辆,视频图像中的车辆检测跟踪和分类
  15. Tabular Editor学习笔记_1
  16. 箱形图适用于哪种数据_Excel 数据可视化:箱形图全面解析!
  17. 分享一个商品历史价格查询的网站
  18. chrome 插件 页面请求转发_使用chrome扩展程序及jsonp实现跨域访问
  19. 关于Linux和Windows一个对比
  20. mybatis generator 自动生成 在线生成器 生成service controller 含基础增删改查 自动生成工具 只需要建表SQL语句

热门文章

  1. lisp 焊接符号标注_焊接符号标注及表示方法-详解aws焊接符号、钢结构焊接符号含义大全...
  2. 大学生转入计算机专业申请书,计算机系大学生助学金申请书1000字
  3. mysql根据出生日期计算年龄并查询
  4. 世界三大短篇小说之王 代表作介绍
  5. NKOJ 4234 三角分形
  6. c语言gcd 简易函数,简单[GCD]用法详细总结(上)
  7. Office2010打开多个excel文件时,开多个excel而不是同一个切换
  8. 2020-03-02
  9. eclipse建java gui工程,Eclipse java swing开发环境搭建教程
  10. 经济学中的100个基本概念