环境需求
PHP >= 8.0
PHP cURL 扩展
PHP OpenSSL 扩展
PHP SimpleXML 扩展
PHP fileinfo 拓展
安装
composer require w7corp/easywechat:^6.17
开始
引入类库、定义变量
use EasyWeChat\MiniApp\Application as MiniApp;//小程序类
use EasyWeChat\Pay\Application as Payment;//微信支付类
protected MiniApp $miniApp;
protected Payment $payApp;
public array $routineConfig = [
'app_id' => '',
'app_secret' => '',
'mch_id' => '',
'secret_key' => '',
'private_key' => '',
'certificate' => '',
];
初始化小程序
public function initMiniApp(): bool
{
try {
$this->miniApp = new MiniApp([
'app_id' => $this->routineConfig['app_id'],
'secret' => $this->routineConfig['app_secret'],
'token' => 'easywechat',
'aes_key' => '......',
// 下面为可选项
// 指定 API 调用返回结果的类型:array(default)/collection/object/raw/自定义类名
'response_type' => 'array',
'log' => [
'level' => 'debug',
'file' => runtime_path() . '/wechat.log',
],
/**
* 是否使用 Stable Access Token
* 默认 false
* https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/mp-access-token/getStableAccessToken.html
* true 使用 false 不使用
*/
'use_stable_access_token' => false,
'http' => [
'verify' => false, // 禁用 SSL 证书验证
'throw' => true, // 状态码非 200、300 时是否抛出异常,默认为开启
'timeout' => 5.0,
// 'base_uri' => 'https://api.weixin.qq.com/', // 如果你在国外想要覆盖默认的 url 的时候才使用,根据不同的模块配置不同的 uri
'retry' => true, // 使用默认重试配置
// 'retry' => [
// // 仅以下状态码重试
// 'status_codes' => [429, 500]
// // 最大重试次数
// 'max_retries' => 3,
// // 请求间隔 (毫秒)
// 'delay' => 1000,
// // 如果设置,每次重试的等待时间都会增加这个系数
// // (例如. 首次:1000ms; 第二次: 3 * 1000ms; etc.)
// 'multiplier' => 3
// ],
],
]);
return true;
} catch (InvalidArgumentException|\Throwable $e) {
Log::error("\n".'小程序配置错误:'."\nerror:".$e->getMessage());
}
return false;
}
初始化微信支付
public function initPay(): bool
{
try {
$this->payApp = new Payment([
'mch_id' => $this->communityInfo['mch_id'],
'secret_key' => $this->communityInfo['secret_key'],// v3 API 秘钥
// 'v2_secret_key' => $info['mch_id'],//v2 API 秘钥
// 商户证书
'private_key' => public_path($this->communityInfo['private_key']),
'certificate' => public_path($this->communityInfo['certificate']),
'log' => [
'level' => 'debug',
'file' => runtime_path() . '/wechat.log',
],
// 平台证书:微信支付 APIv3 平台证书,需要使用工具下载
// 下载工具:https://github.com/wechatpay-apiv3/CertificateDownloader
'platform_certs' => [
(string)($this->communityInfo['pub_key_id']) => public_path($this->communityInfo['pub_key']),
// 如果是「平台证书」模式
// 可简写使用平台证书文件绝对路径
// '/path/to/wechatpay/cert.pem',
// 如果是「微信支付公钥」模式
// 使用Key/Value结构, key为微信支付公钥ID,value为微信支付公钥文件绝对路径
// "{$pubKeyId}" => '/path/to/wechatpay/pubkey.pem',
],
/**
* 接口请求相关配置,超时时间等,具体可用参数请参考:
* https://github.com/symfony/symfony/blob/5.3/src/Symfony/Contracts/HttpClient/HttpClientInterface.php
*/
'http' => [
'verify' => false, // 禁用 SSL 证书验证
'throw' => true, // 状态码非 200、300 时是否抛出异常,默认为开启
'timeout' => 5.0,
// 'base_uri' => 'https://api.mch.weixin.qq.com/',// 如果你在国外想要覆盖默认的 url 的时候才使用,根据不同的模块配置不同的 base_uri
],
]);
return true;
} catch (InvalidArgumentException|\Throwable $e) {
Log::error("\n".'小程序配置错误:'."\nerror:".$e->getMessage());
}
return false;
}
根据CODE获取OpenId
前端传过来的code
$code = $request->input('code','');
$this->initMiniApp()
$account = $this->miniApp->getAccount();
//获取appid
$account->getAppId();
$utils = $this->miniApp->getUtils();
根据code获取openid等
$res = $utils->codeToSession($code);
获取下单参数
use app\extend\SnowFlake;
public function orderPay(Request $request): Response
{
if(!$this->initPay()){
return $this->fail('支付初始化失败');
}
try {
$domain = 'http://aaa.com/';
$orderData = [
"mchid" => $this->communityInfo['mch_id'], // <---- 请修改为您的商户号
"out_trade_no" => SnowFlake::generateId(),
"appid" => $this->communityInfo['app_id'], // <---- 请修改为服务号的 appid
"description" => $this->communityInfo['pay_desc'],
"notify_url" => $domain."routine/notify/vipNotify/".$this->communityInfo['communityid'],
"amount" => [
"total" => (int)bcmul($rechargeInfo['price'] , 100,0),
"currency" => "CNY"
],
"payer" => [
"openid" => $userInfo['openid'] // <---- 请修改为服务号下单用户的 openid
]
];
$res = $this->payApp->getClient()->postJson("v3/pay/transactions/jsapi", $orderData)->toArray();
if(empty($res)){
return $this->fail('微信支付异常');
}
$payData = $this->payApp->getUtils()->buildMiniAppConfig($res['prepay_id'], $this->communityInfo['app_id']); // 返回数组
return $this->success($payData);
} catch (\Throwable $e) {
return $this->fail($e->getMessage());
}
return $this->fail();
}
UNIAPP拉起支付
uni.requestPayment({
provider: 'wxpay',
timeStamp: res.data.timeStamp,
nonceStr: res.data.nonceStr,
package: res.data.package,
signType: res.data.signType,
paySign: res.data.paySign,
success: function(res) {
uni.showLoading({
mask:true,
title: "支付中"
});
setTimeout(() => {
uni.hideLoading();
}, 2000);
},
fail: function(err) {
console.log('fail:' + JSON.stringify(err));
}
});
支付回调
use EasyWeChat\Pay\Message;
use plugin\saiadmin\app\middleware\CrossDomain;
use support\annotation\Middleware;
use support\annotations\Annotations\RequestMapping;
use support\annotations\Annotations\RestController;
use support\Log;
use support\Request;
use support\Response;
use Symfony\Component\HttpFoundation\HeaderBag;
use Symfony\Component\HttpFoundation\Request as SymfonyRequest;
#[Apidoc\Title("支付回调类")]
#[RestController("/routine/notify")]
#[Middleware(CrossDomain::class)]
class NotifyController extends BaseController
{
#[Apidoc\Title("VIP支付回调")]
#[Apidoc\Url("/routine/notify/vipNotify/444")] // 保持与路由一致
#[Apidoc\Method("POST,GET")]
#[Apidoc\ParamType("formdata")]
#[Apidoc\Param(name: "communityid", type: "string", desc: "小区ID", required: true)]
#[RequestMapping(["/vipNotify/{communityid}"], methods: ['GET', 'POST', 'OPTIONS'])]
public function notify(Request $request,$communityid): Response
{
try {
if(!$communityid){
Log::error('小区ID不能为空:'.$communityid);
return $this->error('小区ID不能为空');
}
if(!$this->initPay()){
Log::error('支付初始化失败:'.$communityid);
return $this->fail('支付初始化失败');
}
$symfony_request = new SymfonyRequest( $request->get(),$request->post(),[],$request->cookie(),[],[],$request->rawBody() );
$header = $request->header();
$symfony_request->headers = new HeaderBag( $header );
$this->payApp->setRequestFromSymfonyRequest($symfony_request);
$server = $this->payApp->getServer();
$server->handlePaid(function (Message $message) {
switch ($message->trade_state) {
case 'SUCCESS'://支付成功
// 建议是拿订单号调用微信支付查询接口,以查询到的订单状态为准
$res = $this->payApp->getClient()->get("v3/pay/transactions/out-trade-no/{$message->out_trade_no}", [
'query' => [
'mchid' => $this->payApp->getMerchant()->getMerchantId()
]
])->toArray();
// Log::info('建议是拿订单号调用微信支付查询接口,以查询到的订单状态为准', $res);
// 5. 严格状态校验
if (empty($res['trade_state']) || $res['trade_state'] !== 'SUCCESS') {
break;
}
//支付成功逻辑
(new UsersVipbillLogic())->paySuccess($res);
break;
case 'NOTPAY'://未支付
break;
case 'REFUND'://转入退款
break;
case 'CLOSED'://已关闭
break;
}
})->serve();
} catch (\Throwable $e) {
Log::info('支付回调验证失败' . $e->getMessage());
}
return $this->success();
}
}
176

被折叠的 条评论
为什么被折叠?



