前面我们学习了使用PHP开发微信支付功能的第一步签名,今天正式进入微信支付功能开发的核心之一,下单。在学习本文之前,你需要先准备一个可以被外部网络访问的域名与空间。没有也没关系,因为我也不会多提关于回调的使用,不是多提,是一句话带过,哈哈哈。顺便告诉各位读者,异步回调通知非常重要,如果处理不好,就会造成坏账,当然,你使用查单方法自己查单也是OK的。好了,开始今天的教程微信支付PHP开发教程三统一下单实例,本文会用到前面的微信支付PHP开发教程二签名算法,不清楚的先去了解。
统一下单接口说明
统一下单接口地址:https://api.mch.weixin.qq.com/pay/unifiedorder
是否需要证书:否
参数详解:
请求参数
字段名 | 变量名 | 必填 | 类型 | 示例值 | 描述 |
---|---|---|---|---|---|
公众账号ID | appid | 是 | String(32) | wxd678efh567hg6787 | 微信支付分配的公众账号ID(企业号corpid即为此appId) |
商户号 | mch_id | 是 | String(32) | 1230000109 | 微信支付分配的商户号 |
设备号 | device_info | 否 | String(32) | 013467007045764 | 自定义参数,可以为终端设备号(门店号或收银设备ID),PC网页或公众号内支付可以传”WEB” |
随机字符串 | nonce_str | 是 | String(32) | 5K8264ILTKCH16CQ2502SI8ZNMTM67VS | 随机字符串,长度要求在32位以内。推荐随机数生成算法 |
签名 | sign | 是 | String(32) | C380BEC2BFD727A4B6845133519F3AD6 | 通过签名算法计算得出的签名值,详见签名生成算法 |
签名类型 | sign_type | 否 | String(32) | MD5 | 签名类型,默认为MD5,支持HMAC-SHA256和MD5。 |
商品描述 | body | 是 | String(128) | 腾讯充值中心-QQ会员充值 | 商品简单描述,该字段请按照规范传递,具体请见参数规定 |
商品详情 | detail | 否 | String(6000) | 商品详细描述,对于使用单品优惠的商户,改字段必须按照规范上传,详见“单品优惠参数说明” | |
附加数据 | attach | 否 | String(127) | 深圳分店 | 附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用。 |
商户订单号 | out_trade_no | 是 | String(32) | 20150806125346 | 商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-|* 且在同一个商户号下唯一。详见商户订单号 |
标价币种 | fee_type | 否 | String(16) | CNY | 符合ISO 4217标准的三位字母代码,默认人民币:CNY,详细列表请参见货币类型 |
标价金额 | total_fee | 是 | Int | 88 | 订单总金额,单位为分,详见支付金额 |
终端IP | spbill_create_ip | 是 | String(64) | 123.12.12.123 | 支持IPV4和IPV6两种格式的IP地址。调用微信支付API的机器IP |
交易起始时间 | time_start | 否 | String(14) | 20091225091010 | 订单生成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010。其他详见时间规则 |
交易结束时间 | time_expire | 否 | String(14) | 20091227091010 | 订单失效时间,格式为yyyyMMddHHmmss,如2009年12月27日9点10分10秒表示为20091227091010。订单失效时间是针对订单号而言的,由于在请求支付的时候有一个必传参数prepay_id只有两小时的有效期,所以在重入时间超过2小时的时候需要重新请求下单接口获取新的prepay_id。其他详见时间规则
建议:最短失效时间间隔大于1分钟 |
订单优惠标记 | goods_tag | 否 | String(32) | WXG | 订单优惠标记,使用代金券或立减优惠功能时需要的参数,说明详见代金券或立减优惠 |
通知地址 | notify_url | 是 | String(256) | http://www.weixin.qq.com/wxpay/pay.php | 异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数。 |
交易类型 | trade_type | 是 | String(16) | JSAPI | JSAPI -JSAPI支付
NATIVE -Native支付 APP -APP支付 说明详见参数规定 |
商品ID | product_id | 否 | String(32) | 12235413214070356458058 | trade_type=NATIVE时,此参数必传。此参数为二维码中包含的商品ID,商户自行定义。 |
指定支付方式 | limit_pay | 否 | String(32) | no_credit | 上传此参数no_credit–可限制用户不能使用信用卡支付 |
用户标识 | openid | 否 | String(128) | oUpF8uMuAJO_M2pxb1Q9zNjWeS6o | trade_type=JSAPI时(即JSAPI支付),此参数必传,此参数为微信用户在商户对应appid下的唯一标识。openid如何获取,可参考【获取openid】。企业号请使用【企业号OAuth2.0接口】获取企业号内成员userid,再调用【企业号userid转openid接口】进行转换 |
电子发票入口开放标识 | receipt | 否 | String(8) | Y | Y,传入Y时,支付成功消息和支付详情页将出现开票入口。需要在微信支付商户平台或微信公众平台开通电子发票功能,传此字段才可生效 |
+场景信息 | scene_info | 否 | String(256) | {“store_info” : { “id”: “SZTX001”, “name”: “腾大餐厅”, “area_code”: “440305”, “address”: “科技园中一路腾讯大厦” }} | 该字段常用于线下活动时的场景信息上报,支持上报实际门店信息,商户也可以按需求自己上报相关信息。该字段为JSON对象数据,对象格式为{“store_info”:{“id”: “门店ID”,”name”: “名称”,”area_code”: “编码”,”address”: “地址” }} ,字段详细说明请点击行前的+展开 |
返回结果
字段名 | 变量名 | 必填 | 类型 | 示例值 | 描述 |
---|---|---|---|---|---|
返回状态码 | return_code | 是 | String(16) | SUCCESS | SUCCESS/FAIL
此字段是通信标识,非交易标识,交易是否成功需要查看result_code来判断 |
返回信息 | return_msg | 是 | String(128) | OK | 当return_code为FAIL时返回信息为错误原因 ,例如
签名失败 参数格式校验错误 |
以下字段在return_code为SUCCESS的时候有返回
字段名 | 变量名 | 必填 | 类型 | 示例值 | 描述 |
---|---|---|---|---|---|
公众账号ID | appid | 是 | String(32) | wx8888888888888888 | 调用接口提交的公众账号ID |
商户号 | mch_id | 是 | String(32) | 1900000109 | 调用接口提交的商户号 |
设备号 | device_info | 否 | String(32) | 013467007045764 | 自定义参数,可以为请求支付的终端设备号等 |
随机字符串 | nonce_str | 是 | String(32) | 5K8264ILTKCH16CQ2502SI8ZNMTM67VS | 微信返回的随机字符串 |
签名 | sign | 是 | String(32) | C380BEC2BFD727A4B6845133519F3AD6 | 微信返回的签名值,详见签名算法 |
业务结果 | result_code | 是 | String(16) | SUCCESS | SUCCESS/FAIL |
错误代码 | err_code | 否 | String(32) | 当result_code为FAIL时返回错误代码,详细参见下文错误列表 | |
错误代码描述 | err_code_des | 否 | String(128) | 当result_code为FAIL时返回错误描述,详细参见下文错误列表 |
以下字段在return_code 和result_code都为SUCCESS的时候有返回
字段名 | 变量名 | 必填 | 类型 | 示例值 | 描述 |
---|---|---|---|---|---|
交易类型 | trade_type | 是 | String(16) | JSAPI | JSAPI -JSAPI支付
NATIVE -Native支付 APP -APP支付 说明详见参数规定 |
预支付交易会话标识 | prepay_id | 是 | String(64) | wx201410272009395522657a690389285100 | 微信生成的预支付会话标识,用于后续接口调用中使用,该值有效期为2小时 |
二维码链接 | code_url | 否 | String(64) | weixin://wxpay/bizpayurl/up?pr=NwY5Mz9&groupid=00 | trade_type=NATIVE时有返回,此url用于生成支付二维码,然后提供给用户进行扫码支付。
注意:code_url的值并非固定,使用时按照URL格式转成二维码即可 |
PHP微信支付下单源码
class Wxpay{ const appid='appid'; const mch_id='商户id'; const key='商户key'; public function GetSign($params){ ksort($params); $str=urldecode( http_build_query($params)."&key=".self::key); return strtoupper(md5($str)); }public function Getpayurl(){ $apiparams=[ 'appid'=>self::appid, 'mch_id'=>self::mch_id, 'body'=>'微信支付测试', 'out_trade_no'=>date('YmdHis'), 'total_fee'=>1, 'spbill_create_ip'=>$_SERVER['SERVER_ADDR'], 'notify_url'=>'http://回调域名/notify.php', 'trade_type'=>'NATIVE', 'nonce_str'=>Util::getNonceStr(), 'attach'=>'微信支付测试', 'product_id'=>10000 ]; $apiparams['sign']=$this->GetSign($apiparams); $xml=Util::ArrToXml($apiparams); $result=Util::postXmlCurl($xml,'https://api.mch.weixin.qq.com/pay/unifiedorder'); $arr=Util::XmlToArr($result); if($arr['return_code']=='SUCCESS'){ return $arr['code_url']; }else{ file_put_contents('./error.txt',$result); return false; } }}
上面使用了一些工具类的方法,如XML数据与数组互转、取随机字符串、post发送xml数据等,下面是工具类的源码。
//工具类 class Util{ /** * 以post方式提交xml到对应的接口url * * @param WxPayConfigInterface $config 配置对象 * @param string $xml 需要post的xml数据 * @param string $url url * @param bool $useCert 是否需要证书,默认不需要 * @param int $second url执行超时时间,默认30s * @throws WxPayException */ public static function postXmlCurl($xml, $url, $useCert = false, $second = 30) { $ch = curl_init(); //设置超时 curl_setopt($ch, CURLOPT_TIMEOUT, $second); curl_setopt($ch,CURLOPT_URL, $url); curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,false); curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,2);//严格校验 //设置header curl_setopt($ch, CURLOPT_HEADER, FALSE); //要求结果为字符串且输出到屏幕上 curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); if($useCert == true){ //设置证书 //使用证书:cert 与 key 分别属于两个.pem文件 //证书文件请放入服务器的非web目录下 $sslCertPath = './cert/apiclient_cert.pem'; $sslKeyPath = './cert/apiclient_key.pem'; curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM'); curl_setopt($ch,CURLOPT_SSLCERT, $sslCertPath); curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM'); curl_setopt($ch,CURLOPT_SSLKEY, $sslKeyPath); } //post提交方式 curl_setopt($ch, CURLOPT_POST, TRUE); curl_setopt($ch, CURLOPT_POSTFIELDS, $xml); //运行curl $data = curl_exec($ch); //返回结果 if($data){ curl_close($ch); return $data; } else { $error = curl_errno($ch); curl_close($ch); } } public static function XmlToArr($xml){ if($xml=='')return ''; libxml_disable_entity_loader(true); $arr=json_decode(json_encode(simplexml_load_string($xml,'SimpleXMLElement',LIBXML_NOCDATA)),true); return $arr; } public static function ArrToXml($arr){ if(!is_array($arr)||count($arr)==0)return ''; $xml = "<xml>"; foreach ($arr as $key=>$val) { if (is_numeric($val)){ $xml.="<".$key.">".$val."</".$key.">"; }else{ $xml.="<".$key."><![CDATA[".$val."]]></".$key.">"; } } $xml.="</xml>"; return $xml; } /** * * 产生随机字符串,不长于32位 * @param int $length * @return 产生的随机字符串 */ public static function getNonceStr($length = 32) { $chars = "abcdefghijklmnopqrstuvwxyz0123456789"; $str =""; for ( $i = 0; $i < $length; $i++ ) { $str .= substr($chars, mt_rand(0, strlen($chars)-1), 1); } return $str; } }
注意了,发送post数据方法中使用了判断是否启用证书文件,这个证书需要自己在微信支付商户后台获取,我这里就不多做说明了。
简单使用
$str=(new Wxpay())->Getpayurl(); echo '<img alt="扫码支付测试" src="https://www.daimadog.com/qrcode.php?cont='.$str.'&rc=L&size=300" style="width:150px;height:150px;"/>';
这里我就不用php生成二维码了,麻烦,直接使用代码狗提供的二维码生成接口,统一是使用的php生成的,更加方便。
关于回调的说明
关于金钱的交易我们都应该慎重,支付宝支付接口提供了两种回调,一种同步,一种异步,微信支付这里只提供了异步回调,在交易完成后,微信服务器会将交易内容数据异步通知我们在下单参数中指定的异步通知地址。在这个通知地址文件中,我们一定要判断返回的签名是否正确,切记一定要判断,否则会被别人刷单哟,具体操作我们后面讲,这里先给大家提个醒。
在这个异步通知文件中,我们一般做交易订单的处理,如入库等,这就看你自己的业务逻辑了。
注意:
1、同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。
2、后台通知交互时,如果微信收到商户的应答不符合规范或超时,微信会判定本次通知失败,重新发送通知,直到成功为止(在通知一直不成功的情况下,微信总共会发起10次通知,通知频率为15s/15s/30s/3m/10m/20m/30m/30m/30m/60m/3h/3h/3h/6h/6h – 总计 24h4m),但微信不保证通知最终一定能成功。
3、在订单状态不明或者没有收到微信支付结果通知的情况下,建议商户主动调用微信支付【查询订单API】确认订单状态。
原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/242423.html