富友支付之微信小程序支付

富友支付之微信小程序支付

业务需求,需要在微信小程序端加个富友支付。这和调微信支付和支付宝支付一个类型模板。按照自己支付需求去找对应的接口和参数:富友支付官方文档。把官方Demo贴出来,方便后续的查阅和学习。这里以微信支付小程序为例。

1.需要的导包

org.apache.httpcomponents

httpclient

4.5.2

org.apache.shiro

shiro-core

1.3.2

org.apache.httpcomponents

httpmime

4.5.2

2.准备支付的一些相关的秘钥,机构id等数据,具体根据官网来,这以富友本身支付公众号秘钥为例。

/**

* 富友支付相关数据信息

*/

public class Const {

//编码

public static String charset = "GBK";

//机构私钥

public static final String INS_PRIVATE_KEY ="MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAJgAzD8fEvBHQTyxUEeK963mjziM\n" +

"WG7nxpi+pDMdtWiakc6xVhhbaipLaHo4wVI92A2wr3ptGQ1/YsASEHm3m2wGOpT2vrb2Ln/S7lz1\n" +

"ShjTKaT8U6rKgCdpQNHUuLhBQlpJer2mcYEzG/nGzcyalOCgXC/6CySiJCWJmPyR45bJAgMBAAEC\n" +

"gYBHFfBvAKBBwIEQ2jeaDbKBIFcQcgoVa81jt5xgz178WXUg/awu3emLeBKXPh2i0YtN87hM/+J8\n" +

"fnt3KbuMwMItCsTD72XFXLM4FgzJ4555CUCXBf5/tcKpS2xT8qV8QDr8oLKA18sQxWp8BMPrNp0e\n" +

"pmwun/gwgxoyQrJUB5YgZQJBAOiVXHiTnc3KwvIkdOEPmlfePFnkD4zzcv2UwTlHWgCyM/L8SCAF\n" +

"clXmSiJfKSZZS7o0kIeJJ6xe3Mf4/HSlhdMCQQCnTow+TnlEhDTPtWa+TUgzOys83Q/VLikqKmDz\n" +

"kWJ7I12+WX6AbxxEHLD+THn0JGrlvzTEIZyCe0sjQy4LzQNzAkEAr2SjfVJkuGJlrNENSwPHMugm\n" +

"vusbRwH3/38ET7udBdVdE6poga1Z0al+0njMwVypnNwy+eLWhkhrWmpLh3OjfQJAI3BV8JS6xzKh\n" +

"5SVtn/3Kv19XJ0tEIUnn2lCjvLQdAixZnQpj61ydxie1rggRBQ/5vLSlvq3H8zOelNeUF1fT1QJA\n" +

"DNo+tkHVXLY9H2kdWFoYTvuLexHAgrsnHxONOlSA5hcVLd1B3p9utOt3QeDf6x2i1lqhTH2w8gzj\n" +

"vsnx13tWqg==";

//机构公钥

public static final String INS_PUBLIC_KEY="MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCYAMw/HxLwR0E8sVBHivet5o84jFhu58aYvqQzHbVompHOsVYYW2oqS2h6OMFSPdgNsK96bRkNf2LAEhB5t5tsBjqU9r629i5/0u5c9UoY0ymk/FOqyoAnaUDR1Li4QUJaSXq9pnGBMxv5xs3MmpTgoFwv+gskoiQliZj8keOWyQIDAQAB";

//富友公钥 用于验签

public static final String FY_PUBLIC_KEY ="MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCBv9K+jiuHqXIehX81oyNSD2RfVn+KTPb7NRT5HDPFE35CjZJd7Fu40r0U2Cp7Eyhayv/mRS6ZqvBT/8tQqwpUExTQQBbdZjfk+efb9bF9a+uCnAg0RsuqxeJ2r/rRTsORzVLJy+4GKcv06/p6CcBc5BI1gqSKmyyNBlgfkxLYewIDAQAB";

//机构号

public static String ins_cd = "08A9999999";

//商户号

public static String mchnt_cd = "0002900F0370542";//0002900F0370542

//终端号

public static String term_id = "";

//终端IP

public static String term_ip = "127.0.0.1";

//异步通知(回调地址)

public static String notify_url = "http://www.wrx.cn";

//下单

public static String fuiou_21_url = "https://fundwx.fuiou.com/preCreate";

//扫码

public static String fuiou_22_url = "https://fundwx.fuiou.com/micropay";

//公众号/服务窗统一下单

public static String fuiou_23_url = "https://fundwx.fuiou.com/wxPreCreate";

//退款

public static String fuiou_24_url = "https://fundwx.fuiou.com/commonRefund";

//资金划拨信息

// public static String fuiou_xx_url = "https://fundwx.fuiou.com/queryChnlPayAmt";

//查询可提现资金

public static String fuiou_27_url = "https://fundwx.fuiou.com/queryWithdrawAmt";

//查询手续费

public static String fuiou_28_url = "https://fundwx.fuiou.com/queryFeeAmt";

//提现

public static String fuiou_29_url = "https://fundwx.fuiou.com/withdraw";

//查询

public static String fuiou_30_url = "https://fundwx.fuiou.com/commonQuery";

}

3.准备相关工具类

public class Utils {

/**

* 准备签名所需字段(并不是每个参数都需要进行签名,具体看官网)

* @param map

* @return

*/

public static Map paraFilter(Map map) {

Map result = new HashMap<>();

if (map == null || map.size() <= 0) {

return result;

}

for (String key : map.keySet()) {

String value = map.get(key);

if (key.equalsIgnoreCase("sign") || (key.length() >= 8 && key.substring(0, 8).equalsIgnoreCase("reserved"))) {

continue;

}

result.put(key, value);

}

return result;

}

/**

* 准备请求生成订单的 拼接地址 参数

* @param map

* @return

*/

public static String createLinkString(Map map) {

List keys = new ArrayList<>(map.keySet());

Collections.sort(keys);

String prestr = "";

for (int i = 0; i < keys.size(); i++) {

String key = keys.get(i);

String value = map.get(key);

if (i == keys.size() - 1) {

//拼接时,不包括最后一个&字符

prestr = prestr + key + "=" + value;

} else {

prestr = prestr + key + "=" + value + "&";

}

}

return prestr;

}

/**

* 获取签名

* @param map 准备签名所需要的字段参数

* @return

* @throws InvalidKeySpecException

* @throws SignatureException

* @throws NoSuchAlgorithmException

* @throws InvalidKeyException

* @throws IOException

*/

public static String getSign(Map map) throws InvalidKeySpecException, SignatureException, NoSuchAlgorithmException, InvalidKeyException, IOException {

Map mapNew = paraFilter(map);

String preSignStr = createLinkString(mapNew);

System.out.println("==============================待签名字符串==============================\r\n" + preSignStr);

String sign = Sign.sign(preSignStr, Const.INS_PRIVATE_KEY);

sign = sign.replace("\r\n", "");

System.out.println("==============================签名字符串==============================\r\n" + sign);

return sign;

}

/**

* 校验返回签名

* @param map

* @param sign

* @return

* @throws Exception

*/

public static Boolean verifySign(Map map, String sign) throws Exception {

Map mapNew = paraFilter(map);

String preSignStr = createLinkString(mapNew);

return Sign.verify(preSignStr.getBytes(Const.charset), Const.FY_PUBLIC_KEY, sign);

}

/**

* 将接收到的xml字符串转map

* @param xmlStr

* @return

*/

public static Map xmlStr2Map(String xmlStr){

Map map = new HashMap();

Document doc;

try {

doc = DocumentHelper.parseText(xmlStr);

Element resroot = doc.getRootElement();

List children = resroot.elements();

if(children != null && children.size() > 0) {

for(int i = 0; i < children.size(); i++) {

Element child = (Element)children.get(i);

// map.put(child.getName(), child.getTextTrim());//会将换行符转换成空格

map.put(child.getName(), child.getStringValue().trim());

}

}

} catch (DocumentException e) {

e.printStackTrace();

}

return map;

}

}

4.准备签名相关的加密工具类

public class Sign {

/**

* sign(RSA签名方法)

* TODO(这里描述这个方法适用条件 – 可选)

*

* @param srcSignPacket URL拼接的参数

* @param privateKey 私钥

* @return String DOM对象

* @Exception 异常对象

*/

public static String sign(String srcSignPacket, String privateKey)

throws IOException, NoSuchAlgorithmException,

InvalidKeySpecException, InvalidKeyException, SignatureException {

// 解密由base64编码的私钥

byte[] bytesKey = (new BASE64Decoder()).decodeBuffer(privateKey);

// 构造PKCS8EncodedKeySpec对象

PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(bytesKey);

// KEY_ALGORITHM 指定的加密算法

KeyFactory keyFactory = KeyFactory.getInstance("RSA");

// 取私钥匙对象

PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);

// 用私钥对信息生成数字签名

Signature signature = Signature.getInstance("MD5WithRSA");

signature.initSign(priKey);

signature.update(srcSignPacket.getBytes(Const.charset));

String sign = (new BASE64Encoder()).encodeBuffer(signature.sign());

return sign;

}

/**

* 校验数字签名

*

* @param data 加密数据

* @param publicKey 公钥

* @param sign 数字签名

* @return 校验成功返回true 失败返回false

* @throws Exception

*/

public static boolean verify(byte[] data, String publicKey, String sign) throws Exception {

// 解密由base64编码的公钥

byte[] keyBytes = decryptBASE64(publicKey);

// 构造X509EncodedKeySpec对象

X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);

// KEY_ALGORITHM 指定的加密算法

KeyFactory keyFactory = KeyFactory.getInstance("RSA");

// 取公钥匙对象

PublicKey pubKey = keyFactory.generatePublic(keySpec);

Signature signature = Signature.getInstance("MD5withRSA");

signature.initVerify(pubKey);

signature.update(data);

// 验证签名是否正常

return signature.verify(decryptBASE64(sign));

}

/**

* BASE64加密

*

* @param key

* @return

* @throws Exception

*/

public static String encryptBASE64(byte[] key) throws Exception {

return (new BASE64Encoder()).encodeBuffer(key);

}

/**

* BASE64解密

*

* @param key

* @return

* @throws Exception

*/

public static byte[] decryptBASE64(String key) throws Exception {

return (new BASE64Decoder()).decodeBuffer(key);

}

}

5.准备通用的http请求工具类

public class HttpUtils {

/**

* 发送 post请求访问本地应用并根据传递参数不同返回不同结果

*/

public void post(String url, Map map, StringBuffer result) {

// 创建默认的httpClient实例.

CloseableHttpClient httpclient = HttpClients.createDefault();

// 创建httppost

HttpPost httppost = new HttpPost(url);

// 创建参数队列

List formparams = new ArrayList<>();

Iterator it=map.keySet().iterator();

while(it.hasNext()){

String key;

String value;

key=it.next().toString();

value=map.get(key);

formparams.add(new BasicNameValuePair(key, value));

}

UrlEncodedFormEntity uefEntity;

try {

uefEntity = new UrlEncodedFormEntity(formparams, Const.charset);

httppost.setEntity(uefEntity);

System.out.println("提交请求 " + httppost.getURI());

CloseableHttpResponse response = httpclient.execute(httppost);

try {

HttpEntity entity = response.getEntity();

if (entity != null && result != null) {

result.append(EntityUtils.toString(entity, Const.charset));

}

// 打印响应状态

System.out.println(response.getStatusLine());

} finally {

response.close();

}

} catch (ClientProtocolException e) {

e.printStackTrace();

} catch (UnsupportedEncodingException e1) {

e1.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

} finally {

// 关闭连接,释放资源

try {

httpclient.close();

} catch (IOException e) {

e.printStackTrace();

}

}

}

}

6.准备微信小程序支付需要请求的参数

public class Builder {

private static RandomNumberGenerator randomNumberGenerator = new SecureRandomNumberGenerator();

private static SecureRandom random = new SecureRandom();

/**

* 统一下单

*

* @return

*/

public static Map buildFuiou21() {

Map map = new HashMap<>();

map.put("version", "1");

map.put("ins_cd", Const.ins_cd);

map.put("mchnt_cd", Const.mchnt_cd);

map.put("term_id", "12345678");

map.put("random_str", randomNumberGenerator.nextBytes().toHex());

map.put("sign", "");

map.put("order_type", "ALIPAY");

map.put("goods_des", "这是一个货物");

map.put("goods_detail", "");

map.put("addn_inf", "");

SimpleDateFormat sdf_no = new SimpleDateFormat("yyyyMMddHHmmssSSS");

Calendar calendar = Calendar.getInstance();

map.put("mchnt_order_no", sdf_no.format(calendar.getTime()) + (int) (random.nextDouble() * 100000));

map.put("curr_type", "");

map.put("order_amt", "1");

map.put("term_ip", Const.term_ip);

SimpleDateFormat sdf_ts = new SimpleDateFormat("yyyyMMddHHmmss");

map.put("txn_begin_ts", sdf_ts.format(calendar.getTime()));

map.put("goods_tag", "");

map.put("notify_url", Const.notify_url);

map.put("reserved_sub_appid", "");

map.put("reserved_limit_pay", "");

return map;

}

/**

* 条码支付下单

*

* @return

*/

public static Map buildFuiou22() {

Map map = new HashMap<>();

map.put("version", "1");

map.put("ins_cd", Const.ins_cd);

map.put("mchnt_cd", Const.mchnt_cd);

map.put("term_id", "12345678");

map.put("random_str", randomNumberGenerator.nextBytes().toHex());

map.put("sign", "");

map.put("order_type", "ALIPAY");

map.put("goods_des", "这是一个货物");

map.put("goods_detail", "");

map.put("addn_inf", "");

SimpleDateFormat sdf_no = new SimpleDateFormat("yyyyMMddHHmmssSSS");

Calendar calendar = Calendar.getInstance();

map.put("mchnt_order_no", sdf_no.format(calendar.getTime()) + (int) (random.nextDouble() * 100000));

map.put("curr_type", "");

map.put("order_amt", "1");

map.put("term_ip", Const.term_ip);

SimpleDateFormat sdf_ts = new SimpleDateFormat("yyyyMMddHHmmss");

map.put("txn_begin_ts", sdf_ts.format(calendar.getTime()));

map.put("goods_tag", "");

map.put("auth_code", "289387279140768146");

map.put("sence", "1");

map.put("reserved_sub_appid", "");

map.put("reserved_limit_pay", "");

return map;

}

/**

* 公众号/服务窗统一下单

*

* @return

*/

public static Map buildFuiou23() {

Map map = new HashMap<>();

map.put("version", "1.0");

map.put("ins_cd", Const.ins_cd);

map.put("mchnt_cd", "6510F5938854");//0001210F0976403富友商户号服务商模式0001210F0976403

map.put("term_id", "88888888");

map.put("random_str", randomNumberGenerator.nextBytes().toHex());

map.put("sign", "");

map.put("goods_des", "这是一个货物");

map.put("goods_detail", "");

map.put("goods_tag", "");

map.put("product_id", "");

map.put("addn_inf", "");

SimpleDateFormat sdf_no = new SimpleDateFormat("yyyyMMddHHmmssSSS");

Calendar calendar = Calendar.getInstance();

map.put("mchnt_order_no", sdf_no.format(calendar.getTime()) + (int) (random.nextDouble() * 100000));

map.put("curr_type", "CNY");

map.put("order_amt", "1");

map.put("term_ip", Const.term_ip);

SimpleDateFormat sdf_ts = new SimpleDateFormat("yyyyMMddHHmmss");

map.put("txn_begin_ts", sdf_ts.format(calendar.getTime()));

map.put("notify_url", Const.notify_url);

map.put("limit_pay", "");

map.put("trade_type", "JSAPI");//微信小程序

// map.put("trade_type","LETPAY");//微信小程序

// map.put("trade_type","FWC");//支付宝服务窗

map.put("openid", "ooIeqs5VwPJnDUYfLweOKcR5AxpE"); //富友公众号 ooIeqs5VwPJnDUYfLweOKcR5AxpE

map.put("sub_openid", "osgI-t3iTLkEdGhhwTwyYy_QiqFM");//服务窗时填buyer_id的值 公众号的osgI-t3iTLkEdGhhwTwyYy_QiqFM

map.put("sub_appid", "wx04bdf63c774e12ce");//公众号的 wx04bdf63c774e12ce

map.put("reserved_fy_term_id", "");

map.put("reserved_expire_minute", "0");

// map.put("reserved_user_creid ","");

map.put("reserved_user_truename", "");

map.put("reserved_user_mobile", "");

map.put("addn_inf", "");

return map;

}

}

7.最后调用支付就行

public class FyPayController {

public static void main(String[] args) throws Exception {

//2.1 统一下单

// run(Builder.buildFuiou21(), Const.fuiou_21_url);

//2.2 条码支付

// run(Builder.buildFuiou22(), Const.fuiou_22_url);

//2.3 公众号/服务窗统一下单 具体请阅读“公众号、服务窗下单必读”

run(Builder.buildFuiou23(), Const.fuiou_23_url);

}

public static void run(Map map, String url) throws Exception {

Map reqs = new HashMap<>();

Map nvs = new HashMap<>();

reqs.putAll(map);

String sign = Utils.getSign(reqs);

reqs.put("sign", sign);

Document doc = DocumentHelper.createDocument();

Element root = doc.addElement("xml");

Iterator it = reqs.keySet().iterator();

while (it.hasNext()) {

String key = it.next().toString();

String value = reqs.get(key);

root.addElement(key).addText(value);

}

// String reqBody = doc.getRootElement().asXML();

String reqBody = "" + doc.getRootElement().asXML();

System.out.println("==============================待编码字符串==============================\r\n" + reqBody);

reqBody = URLEncoder.encode(reqBody, Const.charset);

System.out.println("==============================编码后字符串==============================\r\n" + reqBody);

nvs.put("req", reqBody);

StringBuffer result = new StringBuffer("");

HttpUtils httpUtils = new HttpUtils();

httpUtils.post(url, nvs, result);

String rspXml = URLDecoder.decode(result.toString(), Const.charset);

System.out.println("==============================响应报文==============================\r\n" + rspXml);

//响应报文验签

Map resMap = Utils.xmlStr2Map(rspXml);

String str = resMap.get("sign");

System.out.println("str:" + str);

System.out.println("验签结果:" + Utils.verifySign(resMap, str));

}

}

回调那边根据自己设置的notify_url的值获取,回调形式:req={encode之后的xml报文} 收到回调后直接在 request 里面获取req参数的值,然后decode一下就得到xml格式报文。具体回调参数,参照官网。

相关创作

兰芝唇膜好用吗?唇膜top榜兰芝唇膜大测评来啦!!!
下载365APP手机客户端

兰芝唇膜好用吗?唇膜top榜兰芝唇膜大测评来啦!!!

📅 07-21 👁️ 2142
“低保”怎么申领?每月能领多少钱?一文看懂
365bet现金信誉网

“低保”怎么申领?每月能领多少钱?一文看懂

📅 07-08 👁️ 1222