一、前言

微信小程序现在成为了增长模式的宠儿,小程序的便利想必看这篇博客的各位都已经了解到了,本篇呢主要介绍支付退款。

其中里面涉及的关键词:

templateId:小程序运营人员申请模版消息时生成的一个标示,后续推送时会使用。

formId:微信推送时给用户的标示,用一次就失效,这个是在小程序端通过表单获取。

accessToken:通过用户验证登录和授权,获取Access Token,为下一步获取用户的OpenID做准备。

openId:在本小程序内用户的唯一标示。

page:指定召回时点击跳转的页面。

content:这个是根据模版定义出来的任务内容。

mchid:微信中的商户号

partner_key:微信中商户中的商户key

二、需求场景

主要用于商户做活动给用户返现或者退款使用,相当于微信支付的逆向。

三、后台准备

需要微信支付中申请的商户号和小程序或者商户号中的主体进行绑定,并且下载证书apiclient_cert.p12,并且需要申请配置ip白名单。

四、需要的jar  pom

            <dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpcore</artifactId><version>4.4.3</version></dependency><dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId><version>4.4</version></dependency><dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpmime</artifactId><version>4.4</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.62</version></dependency><dependency><groupId>dom4j</groupId><artifactId>dom4j</artifactId><version>1.6.2</version></dependency>

五、上代码

a.主体代码

public class WeixinWithdrawClient {private static final Logger LOGGER = new Logger(WeixinWithdrawClient.class);/*** 微信提现(企业付款)*/public GenericResult<Void> weixinWithdraw(String openId, String appid, String desc, String secret, String partnerkey, String amount,String mechantNo, String realPath,String ip){LOGGER.info("微信提现企业付款入参openId=",openId," ,desc=", desc," ,amount=",amount);if (StringUtils.isNotBlank(desc) && StringUtils.isNotBlank(amount) && StringUtils.isNotBlank(ip) && StringUtils.isNotBlank(openId)) {String xmlInfo = enterprisePay(openId,amount,ip,desc,appid,secret,partnerkey,mechantNo);LOGGER.info("构造微信提现入参openId=",openId," ,xmlInfo=", FastJsonUtils.toJSONNoFeatures(xmlInfo));try {CloseableHttpResponse response =  HttpUtil.Post(WeixinConfigConstans.request_url, xmlInfo, true,  realPath);String transfersXml = EntityUtils.toString(response.getEntity(), "utf-8");LOGGER.info("微信提现出参openId=",openId," ,transfersXml=",transfersXml);Map<String, String> transferMap = XmlUtils.readStringXmlOut(transfersXml);if (transferMap.size()>0) {if (transferMap.get("result_code").equals("SUCCESS") && transferMap.get("return_code").equals("SUCCESS")) {//成功需要进行的逻辑操作,push  提现记录return new GenericResult<>(CommonResponseEnum.SUCCESS,null);}}} catch (Exception e) {LOGGER.info("调用微信提现接口异常openId=",openId," ,e=",e);}}return new GenericResult<>(CommonResponseEnum.FAILED,null);}public static String enterprisePay(String openId, String price, String ip,String desc,String  appId,String secret,String partnerkey,String merchantNo){Long currTime = System.currentTimeMillis();// 六位随机数String strRandom = CommonUtils.generateVerificationCode(6);// 12位序列号,可以自行调整。String nonce_str = currTime + strRandom;String casNo = strRandom + currTime;//签名准备SortedMap<String, String> packageParams = getStringStringSortedMap(openId, price, ip, desc, appId, merchantNo, nonce_str, casNo);LOGGER.info("签名参数构造openId=",openId," ,packageParams=", FastJsonUtils.toJSONNoFeatures(packageParams));//参数以及初始化RequestHandler reqHandler = new RequestHandler(null, null);reqHandler.init(appId, secret, partnerkey);//构造XML参数return getStringParam(openId, price, ip, desc, appId, merchantNo, nonce_str, casNo, packageParams, reqHandler);}/*** 构造微信入参* @param openId* @param price* @param ip* @param desc* @param appId* @param merchantNo* @param nonce_str* @param casNo* @param packageParams* @param reqHandler* @return*/private static String getStringParam(String openId, String price, String ip, String desc, String appId, String merchantNo, String nonce_str, String casNo, SortedMap<String, String> packageParams, RequestHandler reqHandler) {String sign = reqHandler.createSign(packageParams);String xml2 = "<xml>";xml2 +="<mch_appid>" +appId+ "</mch_appid>";xml2 +="<mchid>"+merchantNo+"</mchid>";xml2 +="<nonce_str>" + nonce_str +"</nonce_str>";xml2 +="<partner_trade_no>"+ casNo +"</partner_trade_no>";//商户订单号xml2 +="<openid>"+ openId +"</openid>";xml2 +="<check_name>NO_CHECK</check_name>";xml2 +="<amount>"+ price +"</amount>";xml2 +="<desc>"+desc+"</desc>";xml2 +="<spbill_create_ip>"+ip+"</spbill_create_ip>";xml2 +="<sign>"+ sign +"</sign>";xml2 +="</xml>";return xml2;}/*** 准备签名* @param openId* @param price* @param ip* @param desc* @param appId* @param merchantNo* @param nonce_str* @param casNo* @return*/private static SortedMap<String, String> getStringStringSortedMap(String openId, String price, String ip, String desc, String appId, String merchantNo, String nonce_str, String casNo) {SortedMap<String, String> packageParams = new TreeMap<String, String>();packageParams.put("mch_appid", appId);packageParams.put("mchid", merchantNo);packageParams.put("nonce_str", nonce_str);packageParams.put("partner_trade_no", casNo);packageParams.put("amount", price);packageParams.put("spbill_create_ip", ip);packageParams.put("openid", openId);packageParams.put("desc", desc);packageParams.put("check_name", "NO_CHECK");return packageParams;}
}

b.HttpUtil请求类

public class HttpUtil {/*** 发送post请求** @param url*            请求地址* @param outputEntity*            发送内容* @param isLoadCert*            是否加载证书*/public static CloseableHttpResponse Post(String url, String outputEntity, boolean isLoadCert, String realPath) throws Exception {Protocol.registerProtocol("https",new Protocol("https", (ProtocolSocketFactory)new SSLProtocolSocketFactory(),443));HttpPost httpPost = new HttpPost(url);// 得指明使用UTF-8编码,否则到API服务器XML的中文不能被成功识别httpPost.addHeader("Content-Type", "text/xml");httpPost.setEntity(new StringEntity(outputEntity, "UTF-8"));if (isLoadCert) {// 加载含有证书的http请求SSLConnectionSocketFactory sslConnectionSocketFactory=CertUtil.initCert(realPath);CloseableHttpResponse closeableHttpResponse=HttpClients.custom().setSSLSocketFactory(sslConnectionSocketFactory).build().execute(httpPost);return closeableHttpResponse;} else {return HttpClients.custom().build().execute(httpPost);}}public static String getLocalIP() {InetAddress addr = null;try {addr = InetAddress.getLocalHost();} catch (UnknownHostException e) {e.printStackTrace();}byte[] ipAddr = addr.getAddress();String ipAddrStr = "";for (int i = 0; i < ipAddr.length; i++) {if (i > 0) {ipAddrStr += ".";}ipAddrStr += ipAddr[i] & 0xFF;}return ipAddrStr;}}

c.加载证书

/*** 加载证书的类* @author* @since 2017/08/16*/
public class CertUtil {/*** 加载证书*/public static SSLConnectionSocketFactory initCert( String realPath) throws Exception {FileInputStream instream = null;KeyStore keyStore = KeyStore.getInstance("PKCS12");try {//加载证书File file=new File(realPath+"/apiclient_cert.p12");instream = new FileInputStream(file);//证书文件地址//商户号mch_idkeyStore.load(instream,mch_id.toCharArray());}catch (Exception e){System.out.println("文件不存在或者文件不可读或者文件是目录");}finally {if (null != instream) {instream.close();}}SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, WeixinConfigConstans.mch_id.toCharArray()).build();SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1"}, null, SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);return sslsf;}
}

d.xml解析类

public class XmlUtils {/*** 得到一个类的内部类* @param clz* @return*/public static Class<?>[] getInnerClasses(Class<?> clz) {Class<?>[] innerClasses = clz.getClasses();if (innerClasses == null) {return null;}List<Class<?>> result = new ArrayList<>();result.addAll(Arrays.asList(innerClasses));for (Class<?> inner : innerClasses) {Class<?>[] innerClz = getInnerClasses(inner);if (innerClz == null) {continue;}result.addAll(Arrays.asList(innerClz));}return result.toArray(new Class<?>[0]);}/*** @description 将xml字符串转换成map* @param xml* @return Map*/public static Map<String, String> readStringXmlOut(String xml) {Map<String, String> map = new HashMap<String, String>();Document doc = null;try {doc = DocumentHelper.parseText(xml); // 将字符串转为XMLElement rootElt = doc.getRootElement(); // 获取根节点@SuppressWarnings("unchecked")List<Element> list = rootElt.elements();// 获取根节点下所有节点for (Element element : list) { // 遍历节点map.put(element.getName(), element.getText()); // 节点的name为map的key,text为map的value}} catch (DocumentException e) {e.printStackTrace();} catch (Exception e) {e.printStackTrace();}return map;}
}

六、感悟与展望

在人口红利消退的下半场,用户增长是公共难题。硅谷出现了“Growth Hacker”,中国出现了“精细化运营”,两岸不约而同的出现了以数据为基础,以人工智能为抓手的新增长模式。潜客发掘,流失召回,用户留存,状态跃迁,一批新的模型被定义出来,成为先进增长模式的关键词。流量为王的时代还没有过去,与君共勉。

注:有些技术细节不便在本文中展示体现,如有问题可以评论私信,必有回响。

微信退款(商家退款到用户零用钱)相关推荐

  1. java微信支付v3系列——8.微信支付之退款成功回调

    目录 java微信支付v3系列--1.微信支付准备工作 java微信支付v3系列--2.微信支付基本配置 java微信支付v3系列--3.订单创建准备操作 java微信支付v3系列--4.创建订单的封 ...

  2. 浅析微信支付:申请退款、退款回调接口、查询退款

    本文是[浅析微信支付]系列文章的第八篇,主要讲解商户如何处理微信申请退款.退款回调.查询退款接口,其中有一些坑的地方,会着重强调. 浅析微信支付系列已经更新七篇了哟-,没有看过的朋友们可以看一下哦. ...

  3. php微信支付分取消订单,PHP实现微信支付和退款

    这次给大家带来PHP实现微信支付和退款,PHP实现微信支付和退款的注意事项有哪些,下面就是实战案例,一起来看一下. 之前有写过几篇文章将微信支付和退款: 1.PHP实现微信支付(jsapi支付)流程 ...

  4. 【微信小程序】退款功能教程(含申请退款和退款回调)

    1.一定要区分小程序和公众号的退款,唯一的区别就是 appid不一样,其他的都是一样的. 不废话,直接写代码了啊. 放大招!!! 然后,需要注意的:最好是把证书放在下面的php的同级或者下级. 证书的 ...

  5. springboot之微信支付与退款

    基于springboot实现小程序微信支付与退款 最近需要再写小程序商城,无可避免的需要用到微信支付与商品售后退款等功能.于是研究了一些大佬的代码之后整合出了这个比较简单的微信支付与退款. 相关内容引 ...

  6. 微信退款及退款通知结果 java

    下面的评论有很多想看我的postSSL的源码,这里奉上 public static String postSSL(String url, String data, String certPath, S ...

  7. 微信退款 支付宝退款

    微信退款文档 https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_4 支付宝退款文档 https://docs.open.alipay ...

  8. 关于微信支付的退款那些事

    关于微信支付的退款那些事 微信支付的退款 需要双向证书 一个是操作人的电脑上  需要安装的证书 以p12为结尾的 另外一个证书是2个,需要放到服务器上 微信支付的退款,在请求接口的时候,会在发起人的电 ...

  9. .NET Core 微信小程序退款——(统一退款)

    点击上方"dotNET名人堂",选择"设为星标" 用学习的姿态,步入工作的状态 继上一篇".NET Core 微信小程序支付--(统一下单)后&quo ...

最新文章

  1. 【ES6】JS第7种数据类型:Symbol
  2. 冒泡排序java代码_看动画学算法之:排序冒泡排序
  3. 数据结构java语言描述朱战立_数据结构——树(Java语言描述)
  4. 好文章系列(都是网上非常好的文章)
  5. vmware安装问题:Microsoft Runtime DLL安装程序未能完成安装
  6. Java中的关键字volatile解析
  7. 【转】3个普通IO识别22个按键试验
  8. 尽点力,虽然也不一定有用
  9. 面试题之TCP三次握手和四次挥手详解
  10. 蓝丝雨零基础学习按键精灵VIP教程合集
  11. 【Pygame小游戏】真香~这款百万销量万人追捧大富翁游戏终于出现了~(赶紧来玩儿)
  12. oos的上传和下载2
  13. vue源码分析系列三:render的执行过程和Virtual DOM的产生
  14. 对JAVA多态的理解
  15. C# 获取移动硬盘和U盘信息
  16. Steering Behaviors
  17. Android视频编辑器(三)给本地视频加水印和美颜滤镜
  18. 论文笔记《End-to-End Training of Hybrid CNN-CRF Models for Stereo》用于立体评估的端到端训练的混合CNN-CRF模型
  19. OpenCV - 自动纠正图片的文字倾斜
  20. 初探_红黑(Red-black)树

热门文章

  1. 【NI Multisim 14.0原理图环境设置——标题栏】
  2. 【Web】HTML学习笔记
  3. tplink显示网络连接已断开_TP link路由器设置里面,宽带拨号一直提示已断开(网络异常)求大神解救...
  4. IPTV关键技术综述
  5. php中的session详解,PHP中的session机制详解
  6. yarn配置淘宝镜像
  7. 微信小程序语音聊天智能对话(核心源码)
  8. js的window.print()打印背景图片,打印背景图片无法显示
  9. Python:from aip import AipOcr ModuleNotFoundError: No module named ‘aip‘
  10. 大唐无双关闭服务器是内部消息吗,关于大唐无双的人气问题 我的建议是限制多开党...