PaypalPayment.php
6.73 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
<?php
/**
* Created by PhpStorm.
* User: lishuai
* Date: 2022/2/15
* Time: 4:23 PM
*/
namespace App\Payment;
use App\Models\MembershipGood;
use App\Models\Order;
use App\Models\User;
use App\Models\UserProfile;
use Carbon\Carbon;
use GuzzleHttp\Client;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Redis;
class PaypalPayment implements PaymentInterface
{
const IS_SANDBOX = true;
public $baseUrl = 'https://api-m.paypal.com';
public $baseUrlSandbox = 'https://api-m.sandbox.paypal.com';
public $clientId = 'AdDRE91WSp5q1fYLODpJduc2mRjA_v6E205SvkfVSOgvr98xLeyDCHY4OPAaSFMK1SHYOfJ4TksHSX1-';
public $secret = 'EDoy_PVrFyobWt9DzAMRMikJwWCXenkWSx9CGFz0MxHt3a8fs6v-LnORMilIbftb2GwBKxOoTVZNBHNR';
/** 访问令牌*/
public $accessToken;
public function __construct()
{
// 初始化时做一些准备工作
$redis = Redis::connection();
$access_token = $redis->get('paypal:access_token');
if ($access_token){
$this->accessToken = $access_token;
}else{
$client = new Client([
'base_uri' => $this->baseUrlSandbox,
'headers'=>[
'Content-Type' => 'application/x-www-form-urlencoded',
'Accept'=>'application/json',
]
]);
$response = $client->post('/v1/oauth2/token',[
'form_params'=>['grant_type' => 'client_credentials'],
'auth' => [$this->clientId, $this->secret],
]);
$body = $response->getBody();
$content = json_decode($body->getContents(),true);
$this->accessToken = $content['access_token'];
$redis->setex('paypal:access_token',$content['expires_in'],$content['access_token']);
}
}
/**
* Setting up the JSON request body for creating the Order. The Intent in the
* request body should be set as "CAPTURE" for capture intent flow.
* @param Order $order
* @return array
*/
private static function buildCreateOrderRequestBody(Order $order)
{
return array(
'intent' => 'CAPTURE', //CAPTURE 商家打算在客户付款后立即扣款。 AUTHORIZE 商家打算在客户付款后授权付款并暂停资金
'application_context' =>
array(
'return_url' => 'https://example.com/return', // todo
'cancel_url' => 'https://example.com/cancel', // todo 这里要询问reason app是否有schema,
'brand_name' => 'Parlando', //覆盖 PayPal 网站上 PayPal 帐户中公司名称的标签。
'locale' => 'en-US', //zh-CN
'landing_page' => 'NO_PREFERENCE', //LOGIN / BILLING / NO_PREFERENCE
'shipping_preference' => 'NO_SHIPPING', //运输偏好
'user_action' => 'PAY_NOW',
),
'purchase_units' =>
array(
0 =>
array(
'description' => $order->description, // 购买说明
'custom_id' => $order->order_sn, //API 调用者提供的外部 ID 可以理解为order_id osnxxxxxx
'soft_descriptor' => 'PayPal Parlando Vip', // 出现在付款人卡对帐单上的对帐单描述符的动态文本 最大长度:22.
'amount' =>
array(
'currency_code' => 'USD', // CNY
'value' => $order->pay_amount, //
),
),
),
);
}
public function prepare(Order $order)
{
// 在PayPal上创建一个订单,它会返回一个订单对象,它有一个订单id
$client = new Client([
'base_uri' => $this->baseUrlSandbox,
'headers'=>[
'Content-Type' => 'application/json',
'Authorization' => 'Bearer ' . $this->accessToken,
'Prefer' => 'return=representation',
]
]);
$response = $client->post('/v2/checkout/orders',[
'json' => self::buildCreateOrderRequestBody($order),
]);
$body = $response->getBody();
$content = json_decode($body->getContents(),true);
/** 更新订单交易号 */
$order->pay_number = $content['id'];
$order->pay_type = 'paypal';
$order->save();
return $content;
}
public function capture($orderId)
{
$client = new Client([
'base_uri' => $this->baseUrlSandbox,
'headers'=>[
'Content-Type' => 'application/json',
'Authorization' => 'Bearer ' . $this->accessToken,
'Prefer' => 'return=representation',
]
]);
$response = $client->post('/v2/checkout/orders/' . $orderId . '/capture');
$body = $response->getBody();
$content = json_decode($body->getContents(),true);
// todo 应该使用队列,异步执行回调程序
if (!$this->notify($orderId)) return false;
return $content;
}
public function notify($orderId)
{
$order = Order::query()->where('pay_number',$orderId)->first();
if (!$order) return false;
/** 修改订单状态*/
$order->pay_time = Carbon::now();
$order->status = Order::PAID;
$order->save();
/** 给用户加会员*/
$goods = MembershipGood::query()->find($order->order_goods->goods_id);
if ($goods->limit_unit == '月') {
$days = intval($goods->limit_days) * 30;// 计算天数
} elseif ($goods->limit_unit == '年') {
$days = intval($goods->limit_days) * 365;// 计算天数
} else {
$days = intval($goods->limit_days) * 1;// 计算天数
}
$user = UserProfile::query()->find($order->user_id);
if ($user->is_vip == 0){
$user->is_vip = 1;
$user->create_vip_time = Carbon::now();
$user->expire_vip_time = Carbon::now()->addDays($days);
}else{
if (Carbon::now()->gte($user->expire_vip_time)){ // 已经过期了
$user->expire_vip_time = Carbon::now()->addDays($days);
}else{
$user->expire_vip_time = Carbon::parse($user->expire_vip_time)->addDays($days);
}
}
$user->buy_number += 1;
$user->buy_amount += $order->pay_amount;
$user->last_buy_time = Carbon::now();
$user->save();
/** 修改订单状态*/
$order->status = Order::DONE;
$order->save();
return true;
}
}