微信支付服务商接入指引

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

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

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

##支付主体

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

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

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

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

##服务商

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

服务商小程序开发文档

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

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

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

微信统一下单请求参数

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

此处我的命名是: WechatUnifiedorderRequest

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

名称描述
appid公众号appid
mch_id服务商号
sub_appid小程序的appId
notify-url微信回调地址
trade_type交易类型 JSAPI:小程序 NATIVE:扫码支付
nonce_str32位随机字符
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不存入map
        Field [] 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>() {

                    @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进行加密,得到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}.
     */
    @Override
    public 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}.
     */
    @Override
    public 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

  • 6
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
PHP微信支付接口是一种用于在网站或应用程序中实现微信支付功能的工具。它支持服务商模式,在这种模式下,开发者可以代替商户接入微信支付,为商户提供支付服务。 通过PHP微信支付接口,服务商可以实现以下功能: 1. 创建子商户:服务商可以通过接口创建子商户,子商户即代表真实的商户,在微信支付平台注册账号后,获取子商户的商户号。 2. 交易支付服务商可以通过接口发起交易支付请求,包括拉起微信支付页面、生成二维码等方式。用户在微信内完成支付后,服务商可以获取支付结果,并进行相应的业务处理。 3. 查询交易状态:服务商可以查询某笔交易的支付状态,包括已支付、未支付等。 4. 退款:服务商可以通过接口进行退款操作,将交易金额退还给用户。 5. 订单查询:服务商可以查询某个商户的订单列表,包括交易时间、金额、状态等信息。 6. 结算功能:服务商可以通过接口进行订单的结算操作,将商户的资金结算到指定的银行账户。 总而言之,PHP微信支付接口支持服务商模式,为服务商提供了丰富的支付功能和业务接口,方便服务商代替商户接入微信支付,进行支付和业务管理。服务商可以通过编写PHP代码,调用相应的接口实现支付功能,并根据业务需要进行支付结果查询、退款、结算等操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值