微信支付V3
public static function pay($order_sn = '11111', $money = '12', $uid = 7)
{
$num=Db::table('order')->where(array('order_no'=>$order_sn))->first();
if(!$num){
return $this->error('网络异常,请重新下单');
}
$openid = Db::table('member')->where(array('id' => $uid))->value('openid');
// $url = 'https://api.mch.weixin.qq.com/v3/pay/transactions/app';
$url = 'https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi';
$data = [
'appid' => self::$wxconfig['app_id'],
'mchid' => self::$wxconfig['mch_id'],
'description' => "订单支付",
'out_trade_no' => $order_sn,
'notify_url' => request()->getSchemeAndHttpHost() . '/api/pay/notify',
'time_expire' => date("Y-m-d\TH:i:sP", (time() + 300)),
'amount' => [
'total' => $money * 100,
'currency' => 'CNY',
],
'payer' => [
'openid' => $openid,
],
];
$prepay_id = self::curl($url, json_encode($data), 'POST');
if (isset($prepay_id['code'])) {
return self::pay_error('error', $prepay_id['message']);
}
$msg = [
'appId' => self::$wxconfig['app_id'], // 这是appid
'timeStamp' => (string)time(), // 这是时间戳
'nonceStr' => self::createNonceStr(), // 这是随机码
'package' => 'prepay_id=' . $prepay_id['prepay_id'], // 这是预支付id
'signType' => 'RSA',
];
$sign = self::sign_SHA256_with_RSA($msg);
$msg['sign'] = $sign;
return $msg;
}
public function notify()
{
$wxData = file_get_contents("php://input");
file_put_contents('log1111.txt', $wxData);
$getCallBackArray = json_decode($wxData, true);
// //获取需要解密字段
$associatedData = $getCallBackArray['resource']['associated_data'];
$nonceStr = $getCallBackArray['resource']['nonce'];
$ciphertext = $getCallBackArray['resource']['ciphertext'];
$ciphertexts = $this->decryptToString($associatedData, $nonceStr, $ciphertext);
$resultArray = json_decode($ciphertexts, true);
if ($resultArray['trade_state'] === 'SUCCESS') {
$out_trade_no = $resultArray['out_trade_no'];
$info = DB::table('order')->where(array('order_no' => $out_trade_no, 'status' => '0'))->first();
if ($info) {
$data['status'] = 1;
$data['pay_time'] = time();
// 预定订单
if ($info->is_reserve == '1') {
$data['pay_succ'] = 2;
} else {
$data['pay_succ'] = 1;
}
Db::beginTransaction();
$msg = Db::table('order')->where(array('order_no' => $out_trade_no))->update($data);
// 处理库存
$good = Db::table('order_good')->where(array('order_id' => $info->id))->get();
foreach ($good as $k => $v) {
if ($v->sku) {
self::good_num($v->good_id, $v->sku, $v->num);
} else {
Db::table('good')->where(array('id' => $v->good_id))->update(['good_num' => DB::raw('good_num - ' . $v->num), 'sold' => DB::raw('sold + ' . $v->num)]);
}
}
if ($msg !== false) {
Db::commit();
if ($data['pay_succ'] == '1') {
$this->reward($info->user_id, $info->order_no, $info->dist_one_money, $info->dist_two_money);
}
exit('<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>');
} else {
Db::rollBack();
return false;
}
} else {
return false;
}
}
}
public static function sign_SHA256_with_RSA($data)
{
if (!in_array('sha256WithRSAEncryption', \openssl_get_md_methods(true))) {
throw new \RuntimeException("当前PHP环境不支持SHA256withRSA");
}
$appId = $data['appId'];
$prepayId = $data['package'];
$nonce = $data['nonceStr']; // 随机数
$timestamp = $data['timeStamp']; // 时间戳
$message =
$appId . "\n" .
$timestamp . "\n" .
$nonce . "\n" .
$prepayId . "\n";
$merchantPrivateKeyFilePath = self::getPrivateKey(base_path() . '/public/cert/apiclient_key.pem');
openssl_sign($message, $raw_sign, $merchantPrivateKeyFilePath, 'sha256WithRSAEncryption');
$sign = base64_encode($raw_sign);
return $sign;
}
public static function getPrivateKey($filepath)
{
return openssl_get_privatekey(file_get_contents($filepath));
}
public static function createNonceStr($length = 16)
{
$chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$str = '';
for ($i = 0; $i < $length; $i++) {
$str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
}
return $str;
}
public static function curl($url, $json_str, $method)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
if ($json_str) {
curl_setopt($ch, CURLOPT_POSTFIELDS, $json_str);
}
$token = self::getAuthorization($url, $json_str, $method);
$header = [
'Authorization: WECHATPAY2-SHA256-RSA2048 ' . $token,
'Accept: application/json',
'Content-Type: application/json; charset=utf-8',
'User-Agent:Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36',
];
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_VERBOSE, 1);
curl_setopt($ch, CURLOPT_HEADER, 1);
$response = curl_exec($ch);
$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
return json_decode(substr($response, $header_size), true);
}
public static function getAuthorization($url, $body, $method)
{
$url_parts = parse_url($url);
$canonical_url = ($url_parts['path'] . (!empty($url_parts['query']) ? "?${url_parts['query']}" : ""));
$timestamp = time();
$nonce_str = md5(time());
$message = $method . "\n" .
$canonical_url . "\n" .
$timestamp . "\n" .
$nonce_str . "\n" .
$body . "\n";
$mch_private_key = file_get_contents(base_path() . '/public/cert/apiclient_key.pem');
openssl_sign($message, $raw_sign, $mch_private_key, 'sha256WithRSAEncryption');
$sign = base64_encode($raw_sign);
return sprintf(
'mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"',
self::$wxconfig['mch_id'],
$nonce_str,
$timestamp,
self::$wxconfig['serial_no'],
$sign
);
}
public function decryptToString($associatedData, $nonceStr, $ciphertext)
{
if (strlen(self::$wxconfig['key']) != 32) {
return false;
}
$ciphertext = \base64_decode($ciphertext);
if (strlen($ciphertext) <= 16) {
return false;
}
// ext-sodium (default installed on >= PHP 7.2)
if (function_exists('\sodium_crypto_aead_aes256gcm_is_available') &&
\sodium_crypto_aead_aes256gcm_is_available()) {
return \sodium_crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, self::$wxconfig['key']);
}
// ext-libsodium (need install libsodium-php 1.x via pecl)
if (function_exists('\Sodium\crypto_aead_aes256gcm_is_available') &&
\Sodium\crypto_aead_aes256gcm_is_available()) {
return \Sodium\crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, self::$wxconfig['key']);
}
// openssl (PHP >= 7.1 support AEAD)
if (PHP_VERSION_ID >= 70100 && in_array('aes-256-gcm', \openssl_get_cipher_methods())) {
$ctext = substr($ciphertext, 0, -16);
$authTag = substr($ciphertext, -16);
return \openssl_decrypt($ctext, 'aes-256-gcm', self::$wxconfig['key'], \OPENSSL_RAW_DATA, $nonceStr,
$authTag, $associatedData);
}
throw new \RuntimeException('AEAD_AES_256_GCM需要PHP 7.1以上或者安装libsodium-php');
}