| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461 |
- <?php
- namespace app\api\service;
- use think\Env;
- use WeChatPay\Builder;
- use WeChatPay\Crypto\Rsa;
- use WeChatPay\Formatter;
- use WeChatPay\Util\PemUtil;
- class WxService extends BaseService
- {
- private static function wxAppConfig()
- {
- return array_merge([
- "appid" => Env::get("wxpay.wx_app_appid") ?? "",
- "app_secret" => Env::get("wxpay.wx_app_secret") ?? "",
- ], WxService::wxMchConfig());
- }
- private static function wxWebConfig()
- {
- return array_merge([
- "appid" => Env::get("wxpay.wx_h5_appid") ?? "",
- "app_secret" => Env::get("wxpay.wx_h5_secret") ?? "",
- ], WxService::wxMchConfig());
- }
- private static function wxAppletConfig()
- {
- return array_merge([
- "appid" => Env::get("wxpay.wx_applet_appid") ?? "",
- "app_secret" => Env::get("wxpay.wx_applet_secret") ?? "",
- ], WxService::wxMchConfig());
- }
- private static function wxMchConfig()
- {
- return [
- "mch_id" => Env::get("wxpay.wx_mch_id") ?? "",
- "mch_v3_api" => Env::get("wxpay.wx_mch_v3_api") ?? "",
- "mch_certificate_serial" => Env::get("wxpay.wx_mch_certificate_serial") ?? "",
- "mch_private_key_file_path" => ROOT_PATH . "certificate" . DS . "wx" . DS . "apiclient_key.pem",
- "platform_certificate_file_path" => ROOT_PATH . "certificate" . DS . "wx" . DS . "apiclient_cert.pem"
- ];
- }
- public static function wxPayConfigByType($type = "app")
- {
- if ($type == "app")
- return self::wxAppConfig();
- if ($type == "web")
- return self::wxWebConfig();
- return self::wxAppletConfig();
- }
- /**
- * h5获取微信用户信息
- * @param $code
- * @return \SResult
- */
- public function wxLoginByWeb($code)
- {
- if (!$code)
- return $this->fail("code不存在!");
- $config = WxService::wxWebConfig();
- $s_result = $this->curl_get("https://api.weixin.qq.com/sns/oauth2/access_token?appid={$config["appid"]}&secret={$config["app_secret"]}&code={$code}&grant_type=authorization_code");
- if (0 === $s_result->code())
- return $s_result;
- $code_of_data = $s_result->data();
- // 请求用户信息
- $userinfo_result = $this->curl_get("https://api.weixin.qq.com/sns/userinfo?access_token={$code_of_data['access_token']}&openid={$code_of_data['openid']}");
- if (0 === $userinfo_result->code())
- return $userinfo_result;
- $userinfo_of_data = $userinfo_result->data();
- $merge_array = array_merge($code_of_data, $userinfo_of_data);
- return $this->ok($merge_array);
- }
- /**
- * Applet获取微信用户信息
- * @param $code
- * @return \SResult
- */
- public function wxLoginByApplet($code)
- {
- if (!$code)
- return $this->fail("code不存在!");
- $config = WxService::wxAppletConfig();
- $s_result = $this->curl_get("https://api.weixin.qq.com/sns/jscode2session?appid={$config["appid"]}&secret={$config["app_secret"]}&js_code={$code}&grant_type=authorization_code");
- if (0 === $s_result->code())
- return $s_result;
- return $this->ok($s_result->data());
- }
- // curl get请求
- private function curl_get($url)
- {
- $curl = curl_init();
- // 设置抓取的url
- curl_setopt($curl, CURLOPT_URL, $url);
- // 设置头文件的信息作为数据流输出
- curl_setopt($curl, CURLOPT_HEADER, false);
- // 超时设置,以秒为单位
- curl_setopt($curl, CURLOPT_TIMEOUT, 1);
- // 设置请求头
- curl_setopt($curl, CURLOPT_HTTPHEADER, [
- 'Accept: application/json',
- ]);
- // 设置获取的信息以文件流的形式返回,而不是直接输出。
- curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
- curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
- curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
- // 执行命令
- $data = curl_exec($curl);
- $error = curl_error($curl);
- curl_close($curl);
- // 显示错误信息
- if ($error)
- return $this->fail($error);
- $response = json_decode($data, true);
- if (isset($response["errcode"]) && $response["errcode"] !== 0)
- return $this->fail($response["errmsg"]);
- return $this->ok($response);
- }
- private function sign($message, $key_path)
- {
- $mch_private_key = file_get_contents($key_path);
- openssl_sign($message, $raw_sign, $mch_private_key, 'sha256WithRSAEncryption');
- return base64_encode($raw_sign);
- }
- public function instance($type = 'web')
- {
- $config = WxService::wxPayConfigByType($type);
- // 从本地文件中加载「商户API私钥」,「商户API私钥」会用来生成请求的签名
- $merchantPrivateKeyFilePath = "file://" . $config["mch_private_key_file_path"];
- $merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE);
- // 从本地文件中加载「微信支付平台证书」,用来验证微信支付应答的签名
- $platformCertificateFilePath = "file://" . $config["platform_certificate_file_path"];
- $platformPublicKeyInstance = Rsa::from($platformCertificateFilePath, Rsa::KEY_TYPE_PUBLIC);
- // 从「微信支付平台证书」中获取「证书序列号」
- $platformCertificateSerial = PemUtil::parseCertificateSerialNo($platformCertificateFilePath);
- // 构造一个 APIv3 客户端实例
- return Builder::factory([
- 'mchid' => $config["mch_id"],// 商户号
- 'serial' => $config["mch_certificate_serial"],// 「商户API证书」的「证书序列号」
- 'privateKey' => $merchantPrivateKeyInstance,
- 'certs' => [
- $platformCertificateSerial => $platformPublicKeyInstance,
- ],
- ]);
- }
- public function webPay($openid, $out_trade_no, $description, $amount)
- {
- try {
- $config = WxService::wxWebConfig();
- $body = [
- 'appid' => $config["appid"],
- 'mchid' => $config['mch_id'],
- 'description' => $description,
- 'out_trade_no' => $out_trade_no,
- 'notify_url' => "https://billiards.xunsoftware.com/api.php/api/callback/wxpay",
- 'amount' => [
- 'total' => fixed2Float($amount * 100),
- 'currency' => 'CNY'
- ],
- 'payer' => [
- "openid" => $openid
- ]
- ];
- $resp = $this->instance("web")
- ->chain('v3/pay/transactions/jsapi')
- ->post(['json' => $body]);
- if (200 === $resp->getStatusCode()) {
- $resp_data = json_decode($resp->getBody()->__toString(), true);
- $res = [
- 'appId' => $config['appid'],
- 'timeStamp' => time(),
- 'nonceStr' => Formatter::nonce(),
- 'package' => 'prepay_id=' . $resp_data['prepay_id'],
- 'signType' => 'RSA'
- ];
- $sign = $this->sign($res['appId'] . "\n" . $res["timeStamp"] . "\n" . $res["nonceStr"] . "\n" . $res["package"] . "\n", $config["mch_private_key_file_path"]);
- $res["paySign"] = $sign;
- return $this->ok($res);
- }
- return $this->fail("发起支付失败!");
- } catch (\Exception $e) {
- // 进行错误处理
- if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
- $r = $e->getResponse();
- return $this->fail($r->getBody()->__toString());
- }
- return $this->fail($e->getMessage());
- }
- }
- public function webRefund($out_trade_no, $description, $total_amount, $refund_amount)
- {
- try {
- $body = [
- 'out_trade_no' => $out_trade_no,
- "out_refund_no" => $out_trade_no,
- "reason" => $description,
- 'notify_url' => "https://billiards.xunsoftware.com/api.php/api/callback/wxrefund",
- 'amount' => [
- 'refund' => fixed2Float($refund_amount * 100),
- 'total' => fixed2Float($total_amount * 100),
- 'currency' => 'CNY'
- ],
- ];
- $resp = $this->instance("web")
- ->chain('v3/refund/domestic/refunds')
- ->post(['json' => $body]);
- if (200 === $resp->getStatusCode()) {
- return $this->ok(json_decode($resp->getBody()->__toString(), true), "发起退款成功!");
- }
- return $this->fail("发起退款失败!");
- } catch (\Exception $e) {
- // 进行错误处理
- if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
- $response = json_decode($e->getResponse()->getBody()->__toString(), true);
- return $this->fail($response["message"]);
- }
- return $this->fail($e->getMessage());
- }
- }
- public function appletPay($openid, $out_trade_no, $description, $amount)
- {
- try {
- $config = WxService::wxAppletConfig();
- $resp = $this->instance("applet")
- ->chain('v3/pay/transactions/jsapi')
- ->post(['json' => [
- 'appid' => $config["appid"],
- 'mchid' => $config['mch_id'],
- 'description' => $description,
- 'out_trade_no' => $out_trade_no,
- 'notify_url' => "https://billiards.xunsoftware.com/api.php/api/callback/wxpay",
- 'amount' => [
- 'total' => fixed2Float($amount * 100),
- 'currency' => 'CNY'
- ],
- 'payer' => [
- "openid" => $openid
- ]
- ]]);
- if (200 === $resp->getStatusCode()) {
- $resp_data = json_decode($resp->getBody()->__toString(), true);
- $res = [
- 'appId' => $config['appid'],
- 'timeStamp' => time(),
- 'nonceStr' => Formatter::nonce(),
- 'package' => 'prepay_id=' . $resp_data['prepay_id'],
- 'signType' => 'RSA'
- ];
- $sign = $this->sign($res['appId'] . "\n" . $res["timeStamp"] . "\n" . $res["nonceStr"] . "\n" . $res["package"] . "\n", $config["mch_private_key_file_path"]);
- $res["paySign"] = $sign;
- return $this->ok($res);
- }
- return $this->fail("发起支付失败!");
- } catch (\Exception $e) {
- // 进行错误处理
- if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
- $r = $e->getResponse();
- return $this->fail($r->getBody()->__toString());
- }
- return $this->fail($e->getMessage());
- }
- }
- public function appletRefund($out_trade_no, $description, $total_amount, $refund_amount)
- {
- try {
- $body = [
- 'out_trade_no' => $out_trade_no,
- 'out_refund_no' => $out_trade_no,
- 'reason' => $description,
- 'notify_url' => 'https://billiards.xunsoftware.com/api.php/api/callback/wxrefund',
- 'amount' => [
- 'refund' => fixed2Float($refund_amount * 100),
- 'total' => fixed2Float($total_amount * 100),
- 'currency' => 'CNY'
- ],
- ];
- $resp = $this->instance("applet")
- ->chain('v3/refund/domestic/refunds')
- ->post(['json' => $body]);
- if (200 === $resp->getStatusCode()) {
- return $this->ok(json_decode($resp->getBody()->__toString(), true), "发起退款成功!");
- }
- return $this->fail("发起退款失败!");
- } catch (\Exception $e) {
- // 进行错误处理
- if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
- $response = json_decode($e->getResponse()->getBody()->__toString(), true);
- return $this->fail($response["message"]);
- }
- return $this->fail($e->getMessage());
- }
- }
- public function appPay($openid, $out_trade_no, $description, $amount)
- {
- try {
- $config = WxService::wxAppConfig();
- $resp = $this->instance('app')
- ->chain('v3/pay/transactions/app')
- ->post(['json' => [
- 'appid' => $config["appid"],
- 'mchid' => $config['mch_id'],
- 'description' => $description,
- 'out_trade_no' => $out_trade_no,
- 'notify_url' => "https://billiards.xunsoftware.com/api.php/api/callback/wxpay",
- 'amount' => [
- 'total' => fixed2Float($amount * 100),
- 'currency' => 'CNY'
- ],
- ]]);
- if (200 === $resp->getStatusCode()) {
- $resp_data = json_decode($resp->getBody()->__toString(), true);
- $res = [
- "appid" => $config['appid'],
- "partnerid" => $config['mch_id'],
- "prepayid" => $resp_data['prepay_id'],
- "package" => "Sign=WXPay",
- "noncestr" => Formatter::nonce(),
- "timestamp" => time(),
- ];
- $res['sign'] = $this->sign($res['appid'] . "\n" . $res["timestamp"] . "\n" . $res["noncestr"] . "\n" . $res["prepayid"] . "\n", $config["mch_private_key_file_path"]);
- return $this->ok($res);
- }
- return $this->fail("发起支付失败!");
- } catch (\Exception $e) {
- // 进行错误处理
- if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
- $r = $e->getResponse();
- $body = json_decode($r->getBody()->__toString(), true);
- return $this->fail($body['message']);
- }
- return $this->fail($e->getMessage());
- }
- }
- public function appRefund($out_trade_no, $description, $total_amount, $refund_amount)
- {
- try {
- $config = WxService::wxAppConfig();
- $body = [
- 'appid' => $config["appid"],
- 'mchid' => $config['mch_id'],
- 'description' => $description,
- 'out_trade_no' => $out_trade_no,
- "out_refund_no" => $out_trade_no,
- "reason" => $description,
- 'notify_url' => "https://billiards.xunsoftware.com/api.php/api/callback/wxrefund",
- 'amount' => [
- 'refund' => $refund_amount * 100,
- "total" => $total_amount * 100,
- 'currency' => 'CNY'
- ]
- ];
- $resp = $this->instance("web")
- ->chain('v3/refund/domestic/refunds')
- ->post(['json' => $body]);
- if (200 === $resp->getStatusCode()) {
- return $this->ok(true, "发起退款成功!");
- }
- return $this->fail("发起退款失败!");
- } catch (\Exception $e) {
- // 进行错误处理
- if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
- $r = $e->getResponse();
- return $this->fail($r->getBody()->__toString());
- }
- return $this->fail($e->getMessage());
- }
- }
- public function deposit($openid, $out_batch_no, $batch_name, $batch_remark, $total_amount)
- {
- $config = self::wxWebConfig();
- try {
- $resp = $this->instance("web")
- ->chain('/v3/transfer/batches')
- ->post(['json' => [
- 'appid' => $config["mch_id"],
- "out_batch_no" => $out_batch_no,
- "batch_name" => $batch_name,
- "batch_remark" => $batch_remark,
- "total_amount" => fixed2Float($total_amount * 100),
- 'total_num' => 1,
- 'transfer_detail_list' => [
- [
- "out_detail_no" => $out_batch_no,
- "transfer_amount" => fixed2Float($total_amount * 100),
- "transfer_remark" => $batch_remark,
- "openid" => $openid,
- ]
- ],
- ]]);
- if (200 === $resp->getStatusCode()) {
- return $this->ok(json_decode($resp->getBody()->__toString(), true), "提现成功!");
- }
- return $this->fail("提现失败!");
- } catch (\Exception $e) {
- // 进行错误处理
- if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
- $r = $e->getResponse();
- return $this->fail(json_decode($r->getBody()->__toString(), true));
- }
- return $this->fail($e->getMessage());
- }
- }
- }
|