微信支付小微商户可以通过小程序《微信买单服务商助手》来进件,也可以通过API接口来进件(详情可查阅小微商户专属接口文档)。
2种方式进件后的商户是有区别的
微信买单进件 | API接口进件 | |
---|---|---|
收款方式 | 只能通过小程序《微信买单》收款 | 只能通过API接口收款 |
收款类型 | 仅支持刷卡支付和扫码支付 | 支持刷卡支付、扫码支付、公众号支付和小程序支付 |
对账平台 | 可以通过小程序《微信买单》和小程序《微信支付商户助手》进行对账,不能登录网页版的服务商平台 | 只可以通过小程序《微信支付商户助手》进行对账,不能登录网页版的服务商平台 |
其他区别 (待进一步了解) |
小微商户进件时,需要对姓名、证件信息、银行卡号、联系方式等敏感信息进行加密,加密前需要先下载敏感信息加密公钥正式(下载方法可以参阅博文nodejs 如何通过API 证书(权威CA颁发)下载敏感信息加密公钥证书?);然后根据微信支付官方提供的敏感信息加密方法说明进行敏感信息加密。说明内容如下:
# 敏感信息字段级加密说明
微信支付商户API使用了**HTTPS**,对商户的请求数据和微信支付的应答数据进行端到端的保护。但是对于包含了敏感信息的参数,如姓名、证件信息、银行卡号、联系方式等,我们增加了**字段加密**的安全机制,保护敏感信息只能被数据的接收方看到。
## 如何进行请求数据的加密
1. 获取微信支付平台证书和对应的平台证书序列号,具体方法见《平台证书下载说明》。
2. 使用微信支付平台证书的公钥,对于需要加密的参数值进行RSA加密。填充方案使用`RSAES-PKCS1-v1_5`。
3. 加密后的秘文,使用base64编码后,作为请求中相应参数的值。注:使用字段加密时,接口会要求上送微信支付平台证书序列号。
## 示例代码
以下示例代码片段展示了JAVA程序如何对输入字符串进行加密
```java
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.RSAPublicKeySpec;import javax.crypto.Cipher;
import java.util.Base64;
public class RSAEncryptDemo {
private static final String ALGORITHOM = "RSA";private static final String CIPHER_PROVIDER = "SunJCE";
private static final String TRANSFORMATION_PKCS1Paddiing = "RSA/ECB/PKCS1Padding";private static final String CHAR_ENCODING = "UTF-8";
private static final String PUBLIC_KEY_FILENAME = "/path/publickey/file";public static byte[] encryptPkcs1padding(PublicKey publicKey, byte[] data) throws Exception {
Cipher ci = Cipher.getInstance(TRANSFORMATION_PKCS1Paddiing, CIPHER_PROVIDER);
ci.init(Cipher.ENCRYPT_MODE, publicKey);
return ci.doFinal(data);
}private static String encodeBase64(byte[] bytes) throws Exception {
return Base64.getEncoder().encodeToString(bytes);
}public static String rsaEncrypt(String Content) throws Exception {
final byte[] PublicKeyBytes = Files.readAllBytes(Paths.get(PUBLIC_KEY_FILENAME));
PublicKey publicKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(PublicKeyBytes));return encodeBase64(encryptPkcs1padding(publicKey, Content.getBytes(CHAR_ENCODING)));
}public static PublicKey getRSAPublicKey(BigInteger modulus, BigInteger publicExp) throws Exception {
KeyFactory fact = KeyFactory.getInstance(ALGORITHOM);
RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec(modulus, publicExp);
return fact.generatePublic(publicKeySpec);
}public static void main(String[] args) throws Exception {
System.out.println("after encrypt: " + rsaEncrypt("helloworld"));
}
}
```
用nodejs实现方法如下(需要使用crypto.constants.RSA_PKCS1_PADDING进行加密):
var wpayFieldEncrypt = function(str){
//str为要加密的字段的详细内容,如:张三
//以下为微信支付敏感信息加密公钥,有效期为5年
//"serial_no": "1252DF",
//"effective_time": "2018-08-03T09:01:29+08:00",
//"expire_time": "2023-08-02T09:01:29+08:00",
//由的wpayGenPkey生成,请参阅博文《nodejs 如何通过API 证书(权威CA颁发)下载敏感信息加密公钥证书?》
var RSA_PUBLIC_KEY = '-----BEGIN CERTIFICATE-----\n'+
'MIID8TCCAtmgAwIBAgIUElLfzXjnmcEpHHEqozjajaad+FUwDQYJKoZIhvcNAQEL\n'+
'BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT\n'+
'FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg\n'+
'Q0EwHhcNMTgwODAzMDEwMTI5WhcNMjMwODAyMDEwMTI5WjCBgjEYMBYGA1UEAwwP\n'+
'VGVucGF53131333333dfd3333333333333333333333333333333333DVQQLDBRU\n'+
'ZW5wYXkuY29tIENBIENlbnRlcjELMAkGA1UEBgwCQ04xEjAQBgNVBAgMCUd1YW5n\n'+
'RG9uZzERMA8G3333333333333333333333333333333GSIb3DQEBAQUAA4IBDwAw\n'+
'ggEKAoIBAQCyDR2trGKjr0NTBBiR1VFs0r3elp0/nOElgOdWxeuUH38y9QFRAfDW\n'+
'FDHal3Vuu1zxOEkVhTIFC7RJQMZ8G+cD8MRIyhfptvDxBugJtfYs5/wre44kK56Z\n'+
'DWoz4S97P0Juw6guN7uGwG95tK/sGC2nmw+WVlENHkE1/OyLetm9LFJiypayToLe\n'+
'Xk12314agjlajgglatjtttaWWWWffaggaljlggggajerrjZtSYiikYr2Upzo7wJi\n'+
'sqqzWF7AxyHsgxWVzx8jkjwCTuH5sd27Jx1rGVN5FgvSBBlnOoBu0fKg1C1uVBLe\n'+
'6H35+mQnddddddddddddddddddddddddddddddddddddddddBgNVHRMEAjAAMAsG\n'+
'A1UdDwQEAwIE8DBlBgNVHR8EXjBcMFqgWKBWhlRodHRwOi8vZXZjYS5pdHJ1cy5j\n'+
'b20uY24vcHVibGljL2l0cnVzY3JsP0NBPTFCRDQyMjBFNTBEQkMwNEIwNkFEMzk3\n'+
'NTQ5ODQ2QzAxQzNFOEVCRDIwDQYJKoZIhvcNAQELBQADggEBAE4sBFcGoN7bYG/v\n'+
's4Ly0IP0/EpIPQmlQWS+eRLlgIxT4dIFqwvGoz/xqbigrHw1mXg+KrfMfgB8oDKT\n'+
'2LuHNsOiwj7Mg9v4pFa2XAw+yp6UfVezrmxGTjXbX5tNJp3voY+eFSx5ZWTJGpKW\n'+
'FoaJYboW3VIFchBu2w3MMSWPnw0bkM3N5SgcU40zNfRHKYUx9WgfrgDcQZoTz3wS\n'+
'yNoAw2RTSC7aC5fBOLggZM5EqBdgNhBE6YrZo2/EU087hI8VfddijZ7d1Bug0F5I\n'+
'6aUGjDxjY4oJ08gr2mWZXpV7lCP0mLElUCuzqxkcXa707VnuTeyl2Ck0b+IN90c3\n'+
'hyOEZEw=\n'+
'-----END CERTIFICATE-----'
//RSA_PKCS1_OAEP_PADDING
var encrypted = crypto.publicEncrypt({key:RSA_PUBLIC_KEY, padding:crypto.constants.RSA_PKCS1_PADDING}, new Buffer(str)).toString('base64');
return encrypted;
//以下为解密,这个是微信支付后台要做的,放在这里仅供参考
var buffer2 = new Buffer(encryptedStr, 'base64')
var decrypted = crypto.privateDecrypt({key: pcert,padding: crypto.constants.RSA_PKCS1_PADDING},buffer2)
//pcert 为密钥
res.send(decrypted.toString("utf8")) //ceshi
return '';
}
提交入驻申请的接口写法如下(uploadMedia函数请参阅博文《nodejs微信支付小微商户申请入驻时,如何实现图片上传接口》;wxPost函数请参阅博文《nodejs调用微信支付API接口的通用函数》):
app.get('/uploadXwsh',function(req,res){
//小微商户入驻接口(参数接收仅为了展示效果,请自行设计接收方式)
var sfzzmTp = req.query.sfz1; //身份证正面图片地址
var sfzfmTp = req.query.sfz2; //身份证背面图片地址
var mddmTp = req.query.md1; //门店店面图片地址
var mddnTp = req.query.md22;//门店店内图片地址
var id_card_copy, id_card_national, store_entrance_pic, indoor_pic;
//上传身份证正面照片
uploadMedia(phone, id, sfzzmTp, function(media){
if(media.indexOf('"code":-1') > 0){
res.send(iconv.decode(media,'utf-8'));
return;
}
id_card_copy = media;
//上传身份证背面照片
uploadMedia(phone, id, sfzfmTp , function(media){
if(media.indexOf('"code":-1') > 0){
res.send(iconv.decode(media,'utf-8'));
return;
}
id_card_national = media;
//上传门店店面照片
uploadMedia(phone, id, mddmTp, function(media){
if(media.indexOf('"code":-1') > 0){
res.send(iconv.decode(media,'utf-8'));
return;
}
store_entrance_pic = media;
//上传门店店内照片
uploadMedia(phone, id, mddnTp, function(media){
if(media.indexOf('"code":-1') > 0){
res.send(iconv.decode(media,'utf-8'));
return;
}
indoor_pic = media;
//接收其他要上传的参数,请自行设计传参规则
var business_code = "业务申请编号的规则请自行设计,需要保证不同商户申请用不同的编号";
var id_card_name = wpayFieldEncrypt(req.query.xm);
var id_card_number = wpayFieldEncrypt(req.query.sfzh);
var id_card_valid_time = '["' + req.query.yxq1 + '","' + (req.query.yxq2 ? req.query.yxq2 : '长期') + '"]';
var account_name = id_card_name;
var account_bank = req.query.khyh;
var bank_address_code = req.query.khcs;
var bank_name = req.query.yhqc;
var account_number = wpayFieldEncrypt(req.query.yhzh);
var store_name = req.query.shmc;
var store_address_code = req.query.mdcs;
var store_street = req.query.mddz;
var merchant_shortname = req.query.shmc;
var service_phone = req.query.tel;
var business = req.query.jylm;
var rate = '0.38%';
var contact = id_card_name;
var contact_phone = wpayFieldEncrypt(req.query.mb);
var postObject = {
version:'2.0',
cert_sn:'这个是下载敏感信息加密密钥证书时返回的加密的平台证书序列号,不是直接在服务商平台的API证书(权威CA颁发)中查看到的那个序列号',
mch_id:'服务商商户号',
nonce_str:'任意32为随机字符串',
sign_type:'HMAC-SHA256',
business_code:business_code,
id_card_copy:id_card_copy,
id_card_national:id_card_national,
id_card_name:id_card_name,
id_card_number:id_card_number,
id_card_valid_time:id_card_valid_time,
account_name:account_name,
account_bank:account_bank,
bank_address_code:bank_address_code,
bank_name:bank_name,
account_number:account_number,
store_name:store_name,
store_address_code:store_address_code,
store_street:store_street,
store_entrance_pic:store_entrance_pic,
indoor_pic:indoor_pic,
merchant_shortname:merchant_shortname,
service_phone:service_phone,
business:business,
rate:rate,
contact:contact,
contact_phone:contact_phone
};
wxPost(postObject,{sign_type:"HMAC-SHA256", path:'/applyment/micro/submit'}, function(dat){
if(getXML(dat, "return_code") != "SUCCESS"){
res.send('{"code":-1,"msg":"' + getXML(dat, "return_msg")+ '"}')
return;
}
if(getXML(dat, "result_code") != "SUCCESS"){
res.send('{"code":-2,"msg":"' + getXML(dat, "err_code_des")+ '"}') //
return;
}
//入驻申请提交成功,返回商户申请单号,后续可通过该申请单号查询审核状态,并在审核状态为“等待签约TO_BE_SIGNED”时,获取签约二维码
res.send('{"code":1,"apmid":"' + getXML(dat, "applyment_id")+ '"}')
});
});
});
});
});
});