2015-08-26 09:41:01 u012833715 阅读数 8502
  • 微信支付开发-微信公众号开发12-微信开发php

    微信公众平台开发之微信支付开发是子恒老师《微信公众平台开发》视频教程的第12部。详细讲解了用php进行微信支付的开发。内容包含获取支付密钥,微信公众号支付开发,扫码支付,微信刷卡支付,异步处理支付结果等等。欢迎反馈,微信/QQ:68183131

    27822 人正在学习 去看看 秦子恒

最近在做微信公众平台的服务器端,测试什么的都比较顺利,申请一个沙盒测试号就能满足一切正式环境的需求。

然而昨天在配置到正式环境中的时候出了问题了,一直都是验证token失败,第一次偶尔会出现URL超时,查了一整晚,一开始以为是权限的问题无法修改配置(我是运营者),但后来看了下安全中心,我们未开启配置权限限制。后来又列了很多原因,最后想起我们的正式环境曾经配置过一个微信管家jeewx(一个java写的微信公众平台管理平台,可视化,很方便,然而没什么卵用,微信已经推出了很多方便开发者的功能,而且我们的项目需要获取openid绑定链接,这个管家也没有直接实现的途径),遂百度之,百度了很久终于找到了C站的一个帖子,看到了我想要的答案。

好了不废话了,先大致讲下这个问题的原因:

由于正式环境绑定过java环境的服务器,于是在腾讯服务器的输出缓存区留下了对应本账号的验证缓存,导致我第二次想去验证我sae上用php搭建的服务器时,验证被阻断(log里面有记录,自己调试也能返回echostr(随机字符串,签名生成材料之一),但就是验证不通过)。我是一只小前端,php和java都不熟,不敢妄自装X,所以以上为大致推测,不敢保证是否完全如我所述,但可以肯定的是,原因肯定在之前那个微信管家上面(好坑啊,有这种问题用户手册里也不写),因为我在微信管家上又验证了3个测试号,结果都是本来用自己的服务器验证的正常,在第三方管理平台验证后,自己的服务器就没办法通过验证了。


讲完了原因,重点来了,怎么解决呢?

很简单,php下是ob_clean();  没错,就是这一小搓API,其目的是为了清除输出缓存区,在自己的代码里添加这个之后自己的代码就能正常验证了。这段代码具体放置位置的话在官方demo里是在valid方法的echo $echoStr之前

	public function valid()
    {
        $echoStr = $_GET["echostr"];

        //valid signature , option
        if($this->checkSignature()){
        	<span style="background-color: rgb(255, 255, 51);">echo $echoStr;</span>
        	exit;
        }
    }
我用的是LaneWechat的框架,也能找到相似的函数isValid,也是在return前加下就好了,不加的话,微信就直接pass你的验证请求,重要的话说三遍,直接pass,直接pass。

java的话我不清楚,但遇到这个问题应该也有对应的解决方案。

关于输出缓冲区,这里有一篇关于c++的blog挺直白的--C++编程对缓冲区的理解

特此留念,防止被坑。

2017-09-08 16:05:33 gehaocai 阅读数 768
  • 微信支付开发-微信公众号开发12-微信开发php

    微信公众平台开发之微信支付开发是子恒老师《微信公众平台开发》视频教程的第12部。详细讲解了用php进行微信支付的开发。内容包含获取支付密钥,微信公众号支付开发,扫码支付,微信刷卡支付,异步处理支付结果等等。欢迎反馈,微信/QQ:68183131

    27822 人正在学习 去看看 秦子恒

第一次访问微信端调用JS-SDK的页面时会报错--invalid signature,这个BUG比较坑,第一次接手微信开发要注意下,获取签名signature时需要以下参数:


另外是微信开发文档附录部分:


第一次访问造成signature不合法的原因是微信跳转至授权验证页面时会带上一些参数,所以生成签名的url必须动态获取,js中使用location.href.split("#")[0]获取动态url,特此谨记。

2019-10-16 17:27:03 xiaozhegaa 阅读数 30770
  • 微信支付开发-微信公众号开发12-微信开发php

    微信公众平台开发之微信支付开发是子恒老师《微信公众平台开发》视频教程的第12部。详细讲解了用php进行微信支付的开发。内容包含获取支付密钥,微信公众号支付开发,扫码支付,微信刷卡支付,异步处理支付结果等等。欢迎反馈,微信/QQ:68183131

    27822 人正在学习 去看看 秦子恒

近日,在开发“微信申请退款”的功能。之前有过微信开发的经验,但是第一次接触“微信退款“这一块的业务,查询了很多的博客资料以及走了很多的弯路。也发现“微信退款”分享的博客并不多。特地写了该博客,希望对你们有帮助。个人浅薄的见解

代码下载地址如下:
链接:https://pan.baidu.com/s/1h6_ZSz5RbFARlY9yxkvj5w
提取码:weft
有任何关于微信开发的问题可以相互交流, 推荐QQ:2172931891 , 另外有微商城、微分销、微信小游戏等系统源码,有需要可以联系免费提供。



一、微信退款Api


https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_4

二、开发准备
(1)证书的准备:java开发需要用到:apiclient_cert.p12证书的,在微信公众号上下载–注意PKCS12证书 是从微信商户平台-》账户设置-》 API安全 中下载的 。
这里写图片描述
【何时用到证书?】与支付不一样,企业支付功能在发送post请求的时候,需要加载自己的一个证书之后,带着证书去请求退款才可以。这里使用到证书–很多人不知道证书在哪里使用
(2)了解好数字签名 — 简单来解释,就是对自己要发送的数据进行加密处理、换句话说假如说你要传递A/B/C,就对这三者进行加密。初开发者的误区:不知道该加密什么数据、观看网上的博客胡乱进行签名,导致签名错误
【温馨提示:】数字签名是一般开发人员容易遇到的错误,记住“你没遇到数字签名错误,都不好意思说自己做过微信退款支付订单查询等功能”。
耐心解决就行
(3)熟悉 从xml–》map,以及map—》xml。因为微信只接受xml数据,java写一个xml不简单,但是写map集合非常简单。而且返回的数据是xml格式。需要转化成开发熟知的map集合
不懂的可以看以下这篇博客
http://blog.csdn.net/xiaozhegaa/article/details/79127283

三、退款API截图解释
~~ 接口说明 + 是否需要证书
这里写图片描述
数字签名说明 – 记住一句话:对要传递的数据进行加密,你要传递什么就要签名什么
第一次开发遇到的坑就是:不了解数字签名、花费了自己很长的时间,特地出来强调一下
证书说明
这里写图片描述

四、开发步骤如下
1.拼凑所需要传递的参数 map集合
2.根据要传递的参数生成自己的签名
3.把签名放到map集合中【因为签名也要传递过去】
4.将当前的map结合转化成xml格式
5.发送请求到微信退款Api。发送请求是一个方法来的
6.解析返回的xml数据===》map集合
7.根据map中的result_code/return_code来判断是否成功与失败

不得不再啰嗦一下。下面设计到签名、mapToXml转化、xmlToMap转化、发送请求到API。这些方法都可以在下面网址看到。这里我Xml转化成Map。我是习惯用一个Bean接收,也有范例的代码,大家模仿能力要强一点。这次贴出来给大家看看吧
http://blog.csdn.net/xiaozhegaa/article/details/79127283

/** 
	* 解析退款申请 
	* 解析的时候自动去掉CDMA 
	* @param xml 
	*/ 
	@SuppressWarnings("unchecked") 
	public static RefundResult getUnifiedorderResult(String xml){ 
				RefundResult unifieorderResult = new RefundResult(); 
			try { 
					StringReader read = new StringReader(xml); 
					// 创建新的输入源SAX 解析器将使用 InputSource 对象来确定如何读取 XML 输入 
					InputSource source = new InputSource(read); 
					// 创建一个新的SAXBuilder 
					SAXBuilder sb = new SAXBuilder(); 
					// 通过输入源构造一个Document 
					Document doc; 
					doc = (Document) sb.build(source); 
				
					Element root = doc.getRootElement();// 指向根节点 
					List<Element> list = root.getChildren(); 
				
					if(list!=null&&list.size()>0){ 
					for (Element element : list) { 
						System.out.println("key是:"+element.getName()+",值是:"+element.getText()); 
						if("return_code".equals(element.getName())){ 
								unifieorderResult.setResult_code(element.getText()); 
							} 
					
						if("return_msg".equals(element.getName())){ 
							unifieorderResult.setReturn_msg(element.getText()); 
							} 
					
						if("result_code".equals(element.getName())){ 
							unifieorderResult.setResult_code(element.getText()); 
							} 
						
						
						if("out_refund_no".equals(element.getName())){ 
							unifieorderResult.setOut_refund_no(element.getText()); 
							} 
						
						if("refund_id".equals(element.getName())){ 
							unifieorderResult.setRefund_id(element.getText()); 
							} 
						
						if("refund_fee".equals(element.getName())){ 
							unifieorderResult.setRefund_fee(element.getText()); 
							} 
						
						if("coupon_refund_fee".equals(element.getName())){ 
							unifieorderResult.setCoupon_refund_fee(element.getText()); 
							} 
						
						if("total_fee".equals(element.getName())){ 
							unifieorderResult.setTotal_fee(element.getText()); 
							} 
						
						if("cash_fee".equals(element.getName())){ 
							unifieorderResult.setCash_fee(element.getText()); 
							} 
						if("err_code_des".equals(element.getName())){ 
							unifieorderResult.setErr_code_des(element.getText()); 
							} 
						}
					}
				

			} catch (JDOMException e) { 
			e.printStackTrace(); 
			} catch (IOException e) { 
			e.printStackTrace(); 
			}catch (Exception e) { 
			e.printStackTrace(); 
			} 
				
			return unifieorderResult; 
		} 

@Test
	public void test2(){
		SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>(); 
		
		packageParams.put("appid", wxconfig.AppID);
		packageParams.put("mch_id", wxconfig.mch_id);
		
		String s = UUID.randomUUID().toString().replace("-", "");
		packageParams.put("nonce_str",s);
		
		String s1 = UUID.randomUUID().toString().replace("-", "");
		packageParams.put("out_refund_no",s1);
		packageParams.put("out_trade_no","ozb5fjjxbwag1akdy0vm108makqhsdmx");
	
		packageParams.put("refund_fee","100");
		packageParams.put("total_fee","100");
		
		packageParams.put("op_user_id",wxconfig.mch_id);
		
		String sign  = WeixinPayBack.createSign("utf-8",packageParams);
		
		packageParams.put("sign", sign);
		
		String reuqestXml = WXPayUtil.getRequestXml(packageParams);
		 
		System.out.println("-----------------"); 
		System.out.println(reuqestXml);
		System.out.println("-----------------"); 
		//发送请求到后台了
		String wxUrl = WeixinPayBack.getRefundURL; 
		try {
			String weixinPost = ClientCustomSSL.doRefund(wxUrl, reuqestXml).toString();
			System.out.println(weixinPost);
			
			RefundResult refundResult = WeixinPayBack.getUnifiedorderResult(weixinPost);  
			    
		    String result =null;
		    if("SUCCESS".equalsIgnoreCase(refundResult.getResult_code())){ 
		    	result = "200"; 
		    	System.out.println("==========处理退款成功=========="); 
	    	}else{ 
	    		result = refundResult.getReturn_msg(); 
	    	} 
		    
		    System.out.println(result);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}  
		
		
		
	}

这里写图片描述
五、结果演示
这里写图片描述
自此,我们就学会了“微信申请退款”的开发,希望对你们有帮助
具体的、在调用改方法自己加上自己的业务逻辑就行了。希望对大家有帮助

//TODO 9.0 操作支付表,把当前的支付的状态变成 退款状态   state 1 ---> 2
//TODO 10 操作预约表,可以把当前的预约状态取消  已支付--->退款
//TODO 11 操作用户表,如果是充值退款的话,把用户的现金 - 当前退款的money
//TODO 12其他等等的操作,生成记录单号之类的
2017-12-07 16:13:08 lwpoor123 阅读数 669
  • 微信支付开发-微信公众号开发12-微信开发php

    微信公众平台开发之微信支付开发是子恒老师《微信公众平台开发》视频教程的第12部。详细讲解了用php进行微信支付的开发。内容包含获取支付密钥,微信公众号支付开发,扫码支付,微信刷卡支付,异步处理支付结果等等。欢迎反馈,微信/QQ:68183131

    27822 人正在学习 去看看 秦子恒

微信分享

当有微信推广活动和页面的时候都会用到微信分享,本文介绍如何在页面上处理微信分享,使用的是js+C#.NET语言。

原理

大体流程是这样的:

  1. 绑定域名
  2. 列表内容
  3. 引入JS文件
  4. 通过config接口注入权限验证配置
  5. 通过ready接口处理成功验证
  6. 通过error接口处理失败验证
  7. 编写对应回调

实现

当然可以结合官网文档一起看

  • 绑定域名

先登录微信公众平台进入“公众号设置”的“功能设置”里填写“JS接口安全域名”。
备注:登录后可在“开发者中心”查看对应的接口权限。

  • 引入JS文件

在需要调用JS接口的页面引入如下JS文件,(支持https):http://res.wx.qq.com/open/js/jweixin-1.2.0.js
备注:支持使用 AMD/CMD 标准模块加载方法加载

  • 通过config接口注入权限验证配置

所有需要使用JS-SDK的页面必须先注入配置信息,否则将无法调用(同一个url仅需调用一次,对于变化url的SPA的web app可在每次url变化时进行调用,目前Android微信客户端不支持pushState的H5新特性,所以使用pushState来实现web app的页面会导致签名失败,此问题会在Android6.2中修复)。

wx.config({

    debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。

    appId: '', // 必填,公众号的唯一标识

    timestamp: , // 必填,生成签名的时间戳

    nonceStr: '', // 必填,生成签名的随机串

    signature: '',// 必填,签名,见附录1

    jsApiList: [] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2

});

这里可以使用ajax请求服务端,返回需要的一些签名信息:

$.ajax({
    type: "get",
    url: "getwxfxSign.ashx?url=" + encodeURIComponent(location.href.split('#')[0]),
    success: function (data) {
        if (data != null) {
            data = JSON.parse(data);
            var timeStamp = data.timeStamp;
            var nonceStr = data.nonceStr;
            var signature = data.signature;

            wxfxConfig.timeStamp = timeStamp;
            wxfxConfig.nonceStr = nonceStr;
            wxfxConfig.signature = signature;
            wxfxConfig.zm_title = "";
            wxfxConfig.zm_summary = "";
            wxfxConfig.zm_share = "";

            wxfxConfig.init();
        }
    }
})

确保你获取用来签名的url是动态获取的,前端需要用js获取当前页面除去’#’hash部分的链接(可用location.href.split(‘#’)[0]获取,而且需要encodeURIComponent),因为页面一旦分享,微信客户端会在你的链接末尾加入其它参数,如果不是动态获取当前链接,将导致分享后的页面签名失败。

wxfxConfig.js

var wxfxConfig = {
    timeStamp: '',
    nonceStr: '',
    signature: '',

    zm_title: '',
    zm_summary: '',
    zm_share: '',
    zm_imgurl:'',
    wxconfig: function () {
        wx.config({
            debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
            appId: '', // 必填,公众号的唯一标识
            timestamp: wxfxConfig.timeStamp, // 必填,生成签名的时间戳
            nonceStr: wxfxConfig.nonceStr, // 必填,生成签名的随机串
            signature: wxfxConfig.signature, // 必填,签名,见附录1
            jsApiList: ['onMenuShareTimeline', 'onMenuShareAppMessage', 'onMenuShareQQ', 'onMenuShareWeibo', 'onMenuShareQZone'] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
        });
    },
    wxready: function () {
        wx.ready(function () {
            wx.onMenuShareTimeline({
                title: wxfxConfig.zm_title,//分享标题
                desc: wxfxConfig.zm_summary,//分享摘要
                link: wxfxConfig.zm_share,//分享链接
                imgUrl: wxfxConfig.zm_imgurl,//分享图片
                trigger: function (res) {
                },
                success: function (res) {
                },
                cancel: function (res) {
                },
                fail: function (res) {
                    alert(JSON.stringify(res));
                }
            });

            wx.onMenuShareAppMessage({
                title: wxfxConfig.zm_title,
                desc: wxfxConfig.zm_summary,
                link: wxfxConfig.zm_share,
                imgUrl: wxfxConfig.zm_imgurl,
                trigger: function (res) {
                },
                success: function (res) {
                },
                cancel: function (res) {
                },
                fail: function (res) {
                    alert(JSON.stringify(res));
                }
            });
            //新加分享到qq,微博,qq空间

            wx.onMenuShareQQ({
                title: wxfxConfig.zm_title,
                desc: wxfxConfig.zm_summary,
                link: wxfxConfig.zm_share,
                imgUrl: wxfxConfig.zm_imgurl,
                success: function (res) {

                },
                cancel: function (res) {

                },
                fail: function (res) {
                    alert(JSON.stringify(res));
                }
            });

            wx.onMenuShareWeibo({
                title: wxfxConfig.zm_title,
                desc: wxfxConfig.zm_summary,
                link: wxfxConfig.zm_share,
                imgUrl: wxfxConfig.zm_imgurl,
                success: function (res) {

                },
                cancel: function (res) {

                },
                fail: function (res) {
                    alert(JSON.stringify(res));
                }
            });


            wx.onMenuShareQZone({
                title: wxfxConfig.zm_title,
                desc: wxfxConfig.zm_summary,
                link: wxfxConfig.zm_share,
                imgUrl: wxfxConfig.zm_imgurl,
                success: function (res) {

                },
                cancel: function (res) {

                },
                fail: function (res) {
                    alert(JSON.stringify(res));
                }
            });
        });
    },
    wxerror: function () {
        wx.error(function (res) {
            alert("接口验证失败,详细信息:\n" + JSON.stringify(res));
        });
    },

    init: function () {
        wxfxConfig.wxconfig();
        wxfxConfig.wxready();
        wxfxConfig.wxerror();
    }

}

getwxfxSign.ashx处理程序

using System;
using System.Web;
using System.Collections.Generic;

public class getwxfxSign : IHttpHandler {

    public void ProcessRequest (HttpContext context) {
        context.Response.ContentType = "text/plain";

        if (string.IsNullOrEmpty(HttpContext.Current.Request.QueryString["url"]))
        {
            return;
        }

        string timeStamp = string.Empty;
        string nonceStr = string.Empty;
        string signature = string.Empty;
        nonceStr = CreateNoncestr();//16位随机字符串
        timeStamp = GetTimeStamp();//时间戳
        string url = HttpContext.Current.Request.QueryString["url"];
        string jsapiTicket = GetJsapiTicket();//获取jsapi_ticket

        Dictionary<string, string> signData = new Dictionary<string, string>() { 
                {"noncestr",nonceStr},
                {"jsapi_ticket",jsapiTicket},
                {"timestamp",timeStamp},
                {"url",url}
            };

        signature = Sign(signData);
        context.Response.Write(Newtonsoft.Json.JsonConvert.SerializeObject(new { timeStamp = timeStamp, nonceStr = nonceStr, signature = signature }));
    }

    public bool IsReusable {
        get {
            return false;
        }
    }

}

获取随机字符串:

public static String CreateNoncestr()
{
    String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    String res = "";
    Random rd = new Random();
    for (int i = 0; i < 16; i++)
    {
        res += chars[rd.Next(chars.Length - 1)];
    }
    return res;
}

获取jsapi_ticket可以参考这里

2018-04-10 22:05:43 hu_feng903 阅读数 3997
  • 微信支付开发-微信公众号开发12-微信开发php

    微信公众平台开发之微信支付开发是子恒老师《微信公众平台开发》视频教程的第12部。详细讲解了用php进行微信支付的开发。内容包含获取支付密钥,微信公众号支付开发,扫码支付,微信刷卡支付,异步处理支付结果等等。欢迎反馈,微信/QQ:68183131

    27822 人正在学习 去看看 秦子恒
在页面中进行微信相关的JS开发,第一步就要实现权限验证。

具体步骤参照官网:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115

主要介绍下遇到到一个坑,一般配置如下:

wx.config({
    debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
    appId: '', // 必填,公众号的唯一标识
    timestamp: , // 必填,生成签名的时间戳
    nonceStr: '', // 必填,生成签名的随机串
    signature: '',// 必填,签名
    jsApiList: [] // 必填,需要使用的JS接口列表
});

其中涉及到signature信息,在前面方法中需要用到url。签名用的url必须是调用JS接口页面的完整URL。 

因此签名是每个页面不一样的(每个页面的url不一样),签名不能共用。一般是在把获取配置(包括签名算法)放在服务端,通过js获取当前页面url做为参数传到server。 服务器根据url生成正确的配置返回页面。

微信分享接口开发

阅读数 900

没有更多推荐了,返回首页