云计算开发者社区

用新浪微博连接

一步搞定

查看: 25574|回复: 24

[原创][申精]SAE实现微信公众号Native方式扫码支付 [复制链接]

Rank: 3Rank: 3

发表于 2014-3-20 09:34:44 |显示全部楼层
本帖最后由 jianjun156 于 2014-3-22 10:34 编辑

分享一下微信Native 方式的支付代码。
1、支付场景:

2、代码中包含四个文件createUrl.php、ArrayToXML.php、returnGoodsUrl.php、notifyUrl.php 。
createUrl.php:创建微信二维码支付链接
returnGoodsUrl.php:用户扫描二维码链接后,微信将链接中的商品ID、openId以及其他信息post到该脚本,该脚本负责返回与商品ID对应的商品信息。
notifyUrl.php :用户支付商品后,微信服务器会将商品信息、支付结果、用户的openId等重要信息以get和post方式传递到该链接上,该脚本接收这些信息,并根据支付信息做发货处理,最后返回给微信服务器success,告知他们,我们已经处理到此通知。否则,微信服务器将会定期重新发起通知。
ArrayToXML.php:该脚本的作用是将数组转换成为xml。


3、通知接口详细说明:
对后台通知交互时,如果微信收到商户的应答不是success 或超时,微信认为通知失败,
微信会通过一定的策略(如30 分钟共8 次)定期重新发起通知,尽可能提高通知的成功率,
但微信不保证通知最终能成功。
由于存在重新发送后台通知的情况,因此同样的通知可能会多次发送给商户系统。商户
系统必须能够正确处理重复的通知。
微信推荐的做法是,当收到通知进行处理时,首先检查对应业务数据的状态,判断该通
知是否已经处理过,如果没有处理过再进行处理,如果处理过直接返回success。在对业务
数据进行状态检查和处理之前,要采用数据锁进行并发控制,以避免函数重入造成的数据混
乱。
目前补单机制的间隔时间为:8s、10s、10s、30s、30s、60s、120s、360s、1000s。
代码下载:

[free][/free]

微信支付文档(公众号)下载:





文章及代码为本人原创,转载请注明出处。





附件: 你需要登录才可以下载或查看附件。没有帐号?立即注册

使用道具 举报

Rank: 3Rank: 3

发表于 2014-3-20 09:36:14 |显示全部楼层
createUrl.php:
<?php
/**
 * @author chantrans
 * 本页面的作用是生成商品二维码链接
 */
//测试
echo createUrl("12314124");

/**
 * 产生随机字符串
 */
function getNonceStr()
{
  $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
  $noceStr = "";
  for ($i = 0; $i < 32; $i++) {
    $noceStr .= $chars[ mt_rand(0, strlen($chars) - 1) ];
  }
  $oldNonceStr = $noceStr;
  return $noceStr;
}
/**
 * 二维码扫码链接构造方式:
 * weixin://wxpay/bizpayurl?sign=XXXXX&appid=XXXXXX&productid=XXXXXX×tamp=XXXXXX&noncestr=XXXXXX
 *
 appid 是字段名称:公众号id;字段来源:商户注册具有支付权限的公众号成功后即可获得;传入方式:由商户直接传入。
 timestamp 是字段名称:时间戳;字段来源:商户生成从1970 年1 月1 日00:00:00 至今的秒数,即当前的时间;由商户生成后传入。取值范围:32 字符以下
 noncestr 是字段名称:随机字符串;字段来源:商户生成的随机字符串;取值范围:长度为32 个字符以下。由商户生成后传入。取值范围:32 字符以下
 productid 是字段名称:商品唯一id;字段来源:商户需要定义并维护自己的商品id,这个id 与一张订单等价,微信后台凭借该id 通过Post商户后台获取交易必须信息。由商户生成后传入。取值范围:32字符以下
 sign 是字段名称:签名;字段来源:对前面的其他字段与appKey 按照字典序排序后,使用SHA1 算法得到的结果。由商户生成后传入。参与sign 签名的字段包括:appid、timestamp、noncestr、productid 以及appkey。
 */
function createUrl($productid){
  $app_id = "wxbce29784bdd01454"; //公众号appid
  $app_key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";//公众号支付请求中用于加密的密钥Key,可验证商户唯一身份,PaySignKey对应于支付场景中的appKey值。
  $nonce_str =getNonceStr();
  $time_stamp = strtotime("now");
  //对所有需要传入的参数加上appkey作一次key=value字典序的排序
  $keyvaluestring = "appid=".$app_id."&appkey=".$app_key."&noncestr=".$nonce_str."&productid=".$productid."×tamp=".$time_stamp;

  $sign = sha1($keyvaluestring)."";
  $url = "weixin://wxpay/bizpayurl?sign=".$sign."&appid=".$app_id."&productid=".$productid."×tamp=".$time_stamp."&noncestr=".$nonce_str."";
  return $url;
}



使用道具 举报

Rank: 3Rank: 3

发表于 2014-3-20 09:38:32 |显示全部楼层
returnGoodsUrl.php:
<?php
include 'ArrayToXML.php';
header('Content-Type:text/xml');
/***
 * 该脚本为当公众该平台接到Native(原生)支付请求时,会调用此回调URL获取商品信息。
 */

/**
第一步:接收微信服务器post过来的信息
(1)OpenId,点击链接准备购买商品的用户openid
(2)AppId,公众帐号的appid
(3)IsSubscribe,标记用户是否订阅该公众帐号,1 为关注,0 为未关注
(4)ProductId,第三方的商品ID号
(5)TimeStamp,时间戳
(6)NonceStr,随机串
(7)AppSignature,参数的加密签名,是根据2.7 支付签名(paySign)生成方法中所讲的签名方式生成的签名
(8)SignMethod,签名方式,目前只支持“SHA1”。该字段不参与签名
 **/
$postdata = file_get_contents("php://input");
$postObj = simplexml_load_string ( $postdata, 'SimpleXMLElement', LIBXML_NOCDATA );
$openId = $postObj->OpenId;
$AppId = $postObj->AppId;
$IsSubscribe = $postObj->IsSubscribe;
$ProductId = $postObj->ProductId;
$TimeStamp = $postObj->TimeStamp;
$NonceStr = $postObj->NonceStr;
$AppSignature = $postObj->AppSignature;
$SignMethod = $postObj->SignMethod;


/**
 * 第二步,生成订单号,并且和商品信息,用户openID等订单信息保存在数据库中
 * 
 */
function createTradeId(){
  $curDateTime = date("YmdHis");
  //date_default_timezone_set(PRC);
  $strDate = date("Ymd");
  $strTime = date("His");
  //4位随机数
  $randNum = rand(1000, 9999);
  //10位序列号,可以自行调整。
  $strReq = $strTime . $randNum;
  /* 商家的定单号 */
  $mch_vno = $curDateTime . $strReq;
  /********************/
  /*todo 保存订单信息到数据库中*/
  /********************/
  return $mch_vno;
}

/**
 * 第三步:生成商品详情pakage
 * @param string $body 商品描述  
 * @param string $total_fee 订单总金额,单位为分。
 * @param string $out_trade_no 商户系统内部的订单号
 * @return $package
 */
function getPackage($body,$total_fee,$out_trade_no){  
  $ip=$_SERVER["REMOTE_ADDR"];
  if($ip=="::1"||empty($ip)){
    $ip="127.0.0.1";
  }
  $banktype = "WX";
  $fee_type = "1";//费用类型,这里1为默认的人民币
  $input_charset = "GBK";//字符集,这里将统一使用GBK
  $notify_url = "http://xxxxxx.com/Wxpay/notify.html";//支付成功后将通知该地址
  $out_trade_no =createTradeId();//订单号,商户需要保证该字段对于本商户的唯一性
  $partner = "XXXXXXXX"; //商户号
  $spbill_create_ip =$ip;//订单生成的机器IP
  $partnerKey = "XXXXXXXXXXXXXXXXXXXXXXXXXXX";//这个值和以上其他值不一样是:签名需要它,而最后组成的传输字符串不能含有它。这个key是需要商户好好保存的。
  //首先第一步:对原串进行签名,注意这里不要对任何字段进行编码。这里是将参数按照key=value进行字典排序后组成下面的字符串,在这个字符串最后拼接上key=XXXX。由于这里的字段固定,因此只需要按照这个顺序进行排序即可。
  $signString = "bank_type=".$banktype."&body=".$body."&fee_type=".$fee_type."&input_charset=".$input_charset."¬ify_url=".$notify_url."&out_trade_no=".$out_trade_no."&partner=".$partner."&spbill_create_ip=".$spbill_create_ip."&total_fee=".$total_fee."&key=".$partnerKey;
  $md5SignValue =  ("" .strtoupper(md5(($signString))));
  //echo $md5SignValue;
  //然后第二步,对每个参数进行url转码。
  $banktype = encodeURIComponent($banktype);
  $body=encodeURIComponent($body);
  $fee_type=encodeURIComponent($fee_type);
  $input_charset = encodeURIComponent($input_charset);
  $notify_url = encodeURIComponent($notify_url);
  $out_trade_no = encodeURIComponent($out_trade_no);
  $partner = encodeURIComponent($partner);
  $spbill_create_ip = encodeURIComponent($spbill_create_ip);
  $total_fee = encodeURIComponent($total_fee);
    
  //然后进行最后一步,这里按照key=value除了sign外进行字典序排序后组成下列的字符串,最后再串接sign=value
  $completeString = "bank_type=".$banktype."&body=".$body."&fee_type=".$fee_type."&input_charset=".$input_charset."¬ify_url=".$notify_url."&out_trade_no=".$out_trade_no."&partner=".$partner."&spbill_create_ip=".$spbill_create_ip."&total_fee=".$total_fee;
  $completeString = $completeString."&sign=".$md5SignValue;
  $oldPackageString = $completeString; //记住package,方便最后进行整体签名时取用
  return $completeString;
}

//模拟js中的encodeURIComponent方法
function encodeURIComponent($str) {
  $revert = array('%21'=>'!', '%2A'=>'*', '%27'=>"'", '%28'=>'(', '%29'=>')');
  return strtr(rawurlencode($str), $revert);
}
/**
第四步:
为了返回Package 数据,回调URL 必须返回一个xml 格式的返回数据,形如:
<xml>
<AppId><![CDATA[wwwwb4f85f3a797777]]></AppId>
<Package><![CDATA[a=1&url=http%3A%2F%2Fwww.qq.com]]></Package>
<TimeStamp> 1369745073</TimeStamp>
<NonceStr><![CDATA[iuytxA0cH6PyTAVISB28]]></NonceStr>
<RetCode>0</RetCode>
<RetErrMsg><![CDATA[ok]]></ RetErrMsg>
<AppSignature><![CDATA[53cca9d47b883bd4a5c85a9300df3da0cb48565c]]>
</AppSignature>
<SignMethod><![CDATA[sha1]]></ SignMethod >
</xml>
对于一些第三方觉得商品已经过期或者其他错误的情况,可以在RetCode 和
RetErrMsg 中体现出来,RetCode 为0 表明正确,可以定义其他错误;当定义其他错误时,
可以在RetErrMsg 中填上UTF8 编码的错误提示信息,比如“该商品已经下架”,客户端会
直接提示出来。
 **/

$data=array(
    "AppId"=>$AppId,
    "Package"=>getPackage("测试商品",100,"201311291504302501231"),
    "TimeStamp"=>strtotime(),
    "NonceStr"=>$NonceStr,
    "RetCode"=>0,  //RetCode 为0 表明正确,可以定义其他错误;当定义其他错误时,可以在RetErrMsg 中填上UTF8 编码的错误提示信息,比如“该商品已经下架”,客户端会直接提示出来。
    "RetErrMsg"=>"正确返回",
    "AppSignature"=>$AppSignature,
    "SignMethod"=>"sha1"
);
//返回生成的xml数据
echo ArrayToXML::arrtoxml($data);

















使用道具 举报

Rank: 3Rank: 3

发表于 2014-3-20 09:39:43 |显示全部楼层
notifyUrl.php:
<?
/**
后台通知通过请求中的notify_url 进行,采用post 机制。返回通知中的参数一致,url
包含如下内容:
见【微信公众号支付】公众号支付接口文档V2.2.pdf 中通知接口部分

同时,在postData 中还将包含xml 数据。数据如下:
<xml>
<OpenId><![CDATA[111222]]></OpenId>
<AppId><![CDATA[wwwwb4f85f3a797777]]></AppId>
<IsSubscribe>1</IsSubscribe>
<TimeStamp> 1369743511</TimeStamp>
<NonceStr><![CDATA[jALldRTHAFd5Tgs5]]></NonceStr>
<AppSignature><![CDATA[bafe07f060f22dcda0bfdb4b5ff756f973aecffa]]>
</AppSignature>
<SignMethod><![CDATA[sha1]]></ SignMethod >
</xml>
商户需要对这些参数进行保存和判断用户的支付状态
*/
// 获取微信通知接口postData信息
$postdata = file_get_contents("php://input"); 
$postObj = simplexml_load_string ( $postdata, 'SimpleXMLElement', LIBXML_NOCDATA );
$trade_state =$_GET ["trade_state"];//支付状态
$out_trade_no = $_GET ["out_trade_no"];//订单号
/*****************  Todo 还有很多其他参数需要保存起来,参数列表详见文档 **************************/
if($trade_state==0){
  echo "success";
}else{
  echo "false";
}

使用道具 举报

Rank: 9Rank: 9Rank: 9

发表于 2014-3-20 11:31:28 |显示全部楼层
感谢分享,这个很厉害啊,很潮的东西

使用道具 举报

Rank: 7Rank: 7Rank: 7

发表于 2014-3-20 11:39:36 |显示全部楼层
确实是很潮的一个东西呐~
♫ 心无杂念,未雨绸缪!♫

使用道具 举报

Rank: 3Rank: 3

发表于 2014-3-20 11:42:21 |显示全部楼层
zhaixiaohu 发表于 2014-3-20 11:31
感谢分享,这个很厉害啊,很潮的东西

这个是给“野兽派花店'公众号做微信支付时候提供的技术支持,SAE上做微信支付有一点就是稳定性不太好,有时候不可访问,就接收不到微信服务器发过来的支付结果通知,其他地方完全可以支撑!

使用道具 举报

Rank: 9Rank: 9Rank: 9

发表于 2014-3-20 11:46:26 |显示全部楼层
jianjun156 发表于 2014-3-20 11:42
这个是给“野兽派花店'公众号做微信支付时候提供的技术支持,SAE上做微信支付有一点就是稳定性不太好,有 ...

你看微信的接口分析,一般是百分之多少?

使用道具 举报

Rank: 3Rank: 3

发表于 2014-3-20 11:56:59 |显示全部楼层
zhaixiaohu 发表于 2014-3-20 11:46
你看微信的接口分析,一般是百分之多少?

有的时候会很高,这点对于支付这种安全性,稳定性很高的场景,威胁还是很大的。

附件: 你需要登录才可以下载或查看附件。没有帐号?立即注册

使用道具 举报

Rank: 9Rank: 9Rank: 9

发表于 2014-3-20 12:59:35 |显示全部楼层
jianjun156 发表于 2014-3-20 11:56
有的时候会很高,这点对于支付这种安全性,稳定性很高的场景,威胁还是很大的。

...

昨天sae有些故障,真不好意思哈,我们会努力提升稳定性

使用道具 举报

Rank: 7Rank: 7Rank: 7

发表于 2014-3-24 18:40:53 |显示全部楼层
写的不错,感谢分享!

使用道具 举报

Rank: 2

发表于 2014-3-24 20:43:16 |显示全部楼层
谢谢分享关于微信二维码支付技术brilliant~
微笑是最俱亲和力的沟通

使用道具 举报

Rank: 1

发表于 2014-4-23 20:26:13 |显示全部楼层
非常感谢
拜读中、、、、、、

使用道具 举报

Rank: 1

发表于 2014-4-24 17:14:08 |显示全部楼层
看示例代码中,支付通知都不做签名校验,我是不是可以伪造一个了呢?

使用道具 举报

Rank: 1

发表于 2014-5-5 16:56:21 |显示全部楼层
拜读一下...很火啊这个现在

使用道具 举报

Rank: 1

发表于 2014-5-6 08:55:29 |显示全部楼层
请问一下,是访问weixin://wxpay/bizpayurl...开头的地址么?这是什么类型的地址啊,能解析么

使用道具 举报

Rank: 3Rank: 3

发表于 2014-5-6 09:53:06 |显示全部楼层
地铁庸医 发表于 2014-5-6 08:55
请问一下,是访问weixin://wxpay/bizpayurl...开头的地址么?这是什么类型的地址啊,能解析么 ...

这是微信自加的协议,只有微信客户端才可以解析。

使用道具 举报

Rank: 1

发表于 2014-5-6 11:12:45 |显示全部楼层
jianjun156 发表于 2014-5-6 09:53
这是微信自加的协议,只有微信客户端才可以解析。

多谢解答,以后还会问更多问题的,也请不吝赐教

使用道具 举报

Rank: 1

发表于 2014-6-30 18:04:57 |显示全部楼层
lz 你好,我现在也在做扫码支付,我是根据规则生成url后,然后调用第三方api 生成一个二维码,但是扫描这个二维码的时候 提示商户后台链接失败,这是什么情况?  我的那个url 在微信的网页调试工具上是可以的,返回ok了

使用道具 举报

Rank: 1

发表于 2014-6-30 18:05:44 |显示全部楼层
lz 能加你个好友么? 请教下

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

Archiver|云计算开发者社区 ( 京ICP证000007-137 )

GMT+8, 2018-1-21 12:21 , Processed in 0.186483 second(s), Total 13, Slave 12 queries .

Powered by Discuz! X2

© 2001-2011 Comsenz Inc.

回顶部