我这个也是参考别人写的。有不明白的朋友可以问我
public function unifiedorder($order_no, $openid, $total_fee, $attach, $order_id, $user_id){
// 当前时间
$time = time()
// 生成随机字符串
$nonceStr = md5($time . $openid)
// API参数
$params = [
'appid' =>$this->appid, //微信分配的小程序id
'attach' =>$attach, //附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用。
'body' =>'会员卡', //募捐描述
'mch_id' =>$this->mchid, //微信支付分配的商户号
'nonce_str' =>$nonceStr, //随机字符串,32位以内
'notify_url' =>$this->notify_url, // base_url() . 'notice.php?s=/task/notify/order/wxapp_id/'.$wxapp_id, // 异步通知地址
'openid' =>$openid, //用户标识;trade_type=JSAPI,此参数必传,用户在商户appid下的唯一标识。
'out_trade_no' =>$order_no, //商户账单号
'spbill_create_ip' =>\request()->ip(), //终端IP;支持IPV4和IPV6两种格式的IP地址。调用微信支付API的机器IP
'total_fee' =>(int)$total_fee * 100, // 价格:单位分 // 价格:单位分
'trade_type' =>'JSAPI', //交易类型
]
// 生成签名
$params['sign'] = $this->makeSign($params) //这个地方最坑,需要的是配置 1、appid和商户号必须是绑定的状态
// 请求API
$url = 'https://api.mch.weixin.qq.com/pay/unifiedorder'
$result = $this->post($url, $this->toXml($params))
$prepay = $this->fromXml($result)
//添加preapay_id
$data = [
'user_id' =>$user_id,
'order_id' =>$order_id,
'attach' =>json_encode($attach),
'prepay_id' =>$prepay['prepay_id'],
]
(new AppleWxPrepay())->addInfo($data)
// 请求失败
if ($prepay['return_code'] === 'FAIL') {
return [API_CODE_NAME =>2000004, API_MSG_NAME =>$prepay['return_msg']]
}
if ($prepay['result_code'] === 'FAIL') {
return [API_CODE_NAME =>2000004, API_MSG_NAME =>$prepay['err_code_des']]
}
// 生成 nonce_str 供前端使用
$paySign = $this->makePaySign($params['nonce_str'], $prepay['prepay_id'], $time)
return [
'prepay_id' =>$prepay['prepay_id'],
'nonceStr' =>$nonceStr,
'timeStamp' =>(string)$time,
'paySign' =>$paySign
]
}
/**
* 生成签名
* @param $values
* @return string 本函数不覆盖sign成员变量,如要设置签名需要调用SetSign方法赋值
*/
private function makeSign($values)
{
//签名步骤一:按字典序排序参数
ksort($values)
$string = $this->toUrlParams($values)
//签名步骤二:在string后加入KEY
$string = $string . '&key=' . $this->apikey
//签名步骤三:MD5加密
$string = md5($string)
//签名步骤四:所有字符转为大写
$result = strtoupper($string)
return $result
}
/**
* 格式化参数格式化成url参数
* @param $values
* @return string
*/
private function toUrlParams($values)
{
$buff = ''
foreach ($values as $k =>$v) {
if ($k != 'sign' &&$v != '' &&!is_array($v)) {
$buff .= $k . '=' . $v . '&'
}
}
return trim($buff, '&')
}
/**
* 模拟POST请求
* @param $url
* @param array $data
* @param bool $useCert
* @param array $sslCert
* @return mixed
*/
public function post($url, $data = [], $useCert = false, $sslCert = [])
{
$header = [
'Content-type: application/jsoncharset=UTF8'
]
$curl = curl_init()
curl_setopt($curl, CURLOPT_URL, $url)
curl_setopt($curl, CURLOPT_HTTPHEADER, $header)
curl_setopt($curl, CURLOPT_HEADER, false)
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1)
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false)
curl_setopt($curl, CURLOPT_POST, TRUE)
curl_setopt($curl, CURLOPT_POSTFIELDS, $data)
if ($useCert == true) {
// 设置证书:cert 与 key 分别属于两个.pem文件
curl_setopt($curl, CURLOPT_SSLCERTTYPE, 'PEM')
curl_setopt($curl, CURLOPT_SSLCERT, $sslCert['certPem'])
curl_setopt($curl, CURLOPT_SSLKEYTYPE, 'PEM')
curl_setopt($curl, CURLOPT_SSLKEY, $sslCert['keyPem'])
}
$result = curl_exec($curl)
curl_close($curl)
return $result
}
/**
* 输出xml字符
* @param $values
* @return bool|string
*/
private function toXml($values)
{
if (!is_array($values) || count($values) <= 0) {
return false
}
$xml = "<xml>"
foreach ($values as $key =>$val) {
if (is_numeric($val)) {
$xml .= "<" . $key . ">" . $val . "</" . $key . ">"
} else {
$xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">"
}
}
$xml .= "</xml>"
return $xml
}
/**
* 将xml转为array
* @param $xml
* @return mixed
*/
private function fromXml($xml)
{
// 禁止引用外部xml实体
libxml_disable_entity_loader(true)
return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true)
}
/**
* 生成paySign
* @param $nonceStr
* @param $prepay_id
* @param $timeStamp
* @return string
*/
private function makePaySign($nonceStr, $prepay_id, $timeStamp)
{
$data = [
'appId' =>$this->appid,
'nonceStr' =>$nonceStr,
'package' =>'prepay_id=' . $prepay_id,
'signType' =>'MD5',
'timeStamp' =>$timeStamp,
]
// 签名步骤一:按字典序排序参数
ksort($data)
$string = $this->toUrlParams($data)
// 签名步骤二:在string后加入KEY
$string = $string . '&key=' . $this->apikey
// 签名步骤三:MD5加密
$string = md5($string)
// 签名步骤四:所有字符转为大写
$result = strtoupper($string)
return $result
}
/*********************************微信回调**********************/
public function getNotify()
{
if (!$xml = file_get_contents('php://input')) {
$this->returnCode(50000001, 'Not found DATA')
}
// 将服务器返回的XML数据转化为数组
$data = $this->fromXml($xml)
$payLog = new ApplePayLog()
// 记录日志
$payLog->addInfo(['content'=>json_encode($xml)])
$payLog->addInfo(['content'=>json_encode($data)])
// 实例化账单模型
$OrderModel = new AppleOrder()
// 账单信息
$orderInfo = $OrderModel->getInfo(['id'=>$data['attach']],'*')
if (empty($orderInfo)) {
$this->returnCode(50000001, '账单不存在')
}
if($orderInfo['pay_status'] != 1 || !empty($orderInfo['pay_time'])){
$this->returnCode(50000001,'订单已支付,请勿再次支付')
}
// 保存微信服务器返回的签名sign
$dataSign = $data['sign']
$return_code = $data['return_code']
$result_code = $data['result_code']
$data['body'] = '会员卡'
$data['spbill_create_ip'] = \request()->ip()
$data['notify_url'] = $this->notify_url
// sign 与 s 参数 不参与签名算法
unset($data['sign'])
unset($data['transaction_id'])
unset($data['coupon_id'])
unset($data['coupon_type'])
unset($data['coupon_count'])
unset($data['coupon_fee'])
unset($data['time_end'])
unset($data['return_code'])
unset($data['result_code'])
unset($data['is_subscribe'])
unset($data['fee_type'])
unset($data['bank_type'])
unset($data['bank_type'])
// 生成签名
$sign = $this->makeSign($data)
// 判断签名是否正确 判断支付状态
if (($sign === $dataSign) &&($return_code == 'SUCCESS') &&($result_code == 'SUCCESS')) {
$OrderModel->startTrans()
try {
// 账单支付成功业务处理
$appleOrderInfo = $OrderModel->where(['id'=>$orderInfo['id']])->lock(true)->find()
$result = $appleOrderInfo->addInfo(['pay_status'=>2,'pay_time'=>time()],['id'=>$orderInfo['id']])
if(!$result){
$OrderModel->rollback()
$this->returnCode(5000003, '修改订单失败,失败原因:'.$OrderModel->getError())
}
$appleUserModel = new AppleUser()
$appleUserInfo = $appleUserModel->where(['openid'=>$orderInfo['openid']])->lock(true)->find()
$appleUser = $appleUserInfo->where(['openid'=>$orderInfo['openid']])->setInc('moxibustion',$orderInfo['moxibustion'])
if(!$appleUser){
$OrderModel->rollback()
$this->returnCode(5000003, '添加会员针灸次数失败,失败原因:'.$appleUserModel->getError())
}
}catch (\Exception $exception){
$OrderModel->rollback()
$this->returnCode(5000003, ' *** 作失败,失败原因:'.$exception->getMessage())
}
$OrderModel->commit()
// 返回状态
die(json(['code'=>0,'支付成功']))
}
// 返回状态
$this->returnCode(2000003, '签名失败')
}
微信公众平台: 微信公众平台
https://mp.weixin.qq.com/
商户平台: 商户平台
https://pay.weixin.qq.com/index.php/core/home/login
开发手册 api 是:
https://pay.weixin.qq.com/wiki/doc/api/index.html
https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_1
生成XML的测试接口: 测试接口
https://pay.weixin.qq.com/wiki/tools/signverify/
32位随机码生成器
1 配置参数, 一个都不能少(这些都是必填的,还有其他选填的参数,也可以随心情填写)
appid 此小程序的唯一标识 例如:wxed9dxxx6d6cxxx9e
body 可以写商品描述 例如:我是商品描述,用户买了两个馒头
mch_id 商户id 需在 商户平台 查看(申请商户平台成功就会得到) 如: 149411***2
nonce_str 随机字符串(别带小数点(没考证 感觉)) 例如:76521019851170500000
notify_url 成功后的通知地址 例如:https://baidu.top/callbackofpay
openid 用户的唯一标识 在用户使用小程序的时候通过其 code 换取 openid
例如: obxgc5CgBbgKDrvcxxxxxJ-xxxxx
out_trade_no 商户订单号 例如:20180805740161 需要无重复
spbill_create_ip 终端ip(服务器的ip)貌似 127.0.0.1 就行
total_fee 此单的交易额度(钱 money) 例如:888 单位是 分钱。及8.88元钱 ( 注意是数值型,非字符型,除此之外其他的都是字符型 )
trade_type 小程序用 'JSAPI' 其他看api
2 将以上参数(必要参数一个不能少)按照 key 字典排序,进行拼接。
例如: appid=wxedxxx2xxx6c03e9e&body=我是商品描述,用户买了两个馒头.&mch_id=149411***2&nonce_str=76521019851170500000 ......
3 在结尾加上 商户的秘钥 mch_key (需在商户平台配置 在:账户中心-->账户设置-->API安全-->API秘钥 ,可以用 32位随机码生成器 生成)
+ "&key=" + mch_key
5 对拼接成的字符串,进行md5加密-->转大写 -->生成 签名 sign
例如: 425F6561A654B366B5519F000CF2AE61
6 将以上参数拼成 xml
let _xmlUnifiedorder = `
${myObj.appid}
${myObj.body}
${myObj.mch_id}
${myObj.nonce_str}
${myObj.notify_url}
${myObj.openid}
${myObj.out_trade_no}
${myObj.spbill_create_ip}
${myObj.total_fee}
${myObj.trade_type}
${sign}
`
7 携带参数 post 访问 https://api.mch.weixin.qq.com/pay/unifiedorder 接口
如果正确 会成功得到 xml格式的 prepay_id ,如果签名错误进行下面的错误查找。
~~:进行错误查找 可以拿出参数在 测试接口 进行测试 ,无异常可以正常生成 xml之后,如果还报错。
试着 更改 商户平台的秘钥 (详细请看第 3 步)。貌似很多人都踩这个雷了 ...
<code>
// 字典排序 key1=xxx&key2=zzz 拼接成字符串
function joinToStr(_obj) {
let keyArr = []
for (let key in _obj) {
keyArr.push(key)
}
keyArr = keyArr.sort()
console.log("keyArrSort--->", keyArr)
// paySign = MD5(appId=wxd678efh567hg6787&nonceStr=5K8264ILTKCH16CQ2502SI8ZNMTM67VS&package=prepay_id=wx2017033010242291fcfe0db70013231072&signType=MD5&timeStamp=1490840662&key=qazwsxedcrfvtgbyhnujmikolp111111) = 22D9B4E54AB1950F51E0649E8810ACD6
// _str 是 paySign 中MD5 包裹的字符串部分
let _str = ""
let len = keyArr.length
for (let i = 0i <leni++) {
if (i == len - 1) {
_str += (keyArr[i] + "=" + _obj[keyArr[i]])
} else {
_str += (keyArr[i] + "=" + _obj[keyArr[i]] + "&")
}
}
return _str
}
</code>
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)