From 80841248e5b4593e91f899c170c2a41d54ee93df Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=85=94=E5=A7=AC=E6=A1=91?=
Date: Sun, 26 Apr 2020 17:20:08 +0800
Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20PayPal=20=E6=94=AF?=
=?UTF-8?q?=E4=BB=98=E6=96=B9=E5=BC=8F?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.env.example | 5 -
app/Http/Controllers/AdminController.php | 5 +
app/Http/Controllers/Gateway/PayJs.php | 23 +--
app/Http/Controllers/Gateway/PayPal.php | 132 ++++++++++++++++++
app/Http/Controllers/PaymentController.php | 3 +
composer.json | 1 +
composer.lock | 63 ++++++++-
config/app.php | 14 +-
config/paypal.php | 30 ++++
resources/views/admin/layouts.blade.php | 2 +-
resources/views/admin/system.blade.php | 66 +++++++++
.../views/admin/ticket/replyTicket.blade.php | 4 +-
.../views/user/components/purchase.blade.php | 6 +-
routes/web.php | 5 +-
sql/mod/20200426.sql | 6 +
15 files changed, 337 insertions(+), 28 deletions(-)
create mode 100644 app/Http/Controllers/Gateway/PayPal.php
create mode 100644 config/paypal.php
create mode 100644 sql/mod/20200426.sql
diff --git a/.env.example b/.env.example
index d051eb4a..c46aa9ff 100644
--- a/.env.example
+++ b/.env.example
@@ -47,8 +47,3 @@ MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
REDIRECT_HTTPS=true
-
-GEETEST_ID=
-GEETEST_KEY=
-NOCAPTCHA_SECRET=
-NOCAPTCHA_SITEKEY=
diff --git a/app/Http/Controllers/AdminController.php b/app/Http/Controllers/AdminController.php
index 890d5bfb..c907fbc5 100644
--- a/app/Http/Controllers/AdminController.php
+++ b/app/Http/Controllers/AdminController.php
@@ -2012,6 +2012,11 @@ EOF;
return Response::json(['status' => 'fail', 'message' => '请先设置【麻瓜宝】必要参数']);
}
break;
+ case 'paypal':
+ if(!self::$systemConfig['paypal_username'] || !self::$systemConfig['paypal_password']){
+ return Response::json(['status' => 'fail', 'message' => '请先设置【PayPal】必要参数']);
+ }
+ break;
default:
return Response::json(['status' => 'fail', 'message' => '未知支付渠道']);
break;
diff --git a/app/Http/Controllers/Gateway/PayJs.php b/app/Http/Controllers/Gateway/PayJs.php
index 7cc08f9e..9552b5f9 100644
--- a/app/Http/Controllers/Gateway/PayJs.php
+++ b/app/Http/Controllers/Gateway/PayJs.php
@@ -10,6 +10,16 @@ use Xhat\Payjs\Payjs as Pay;
class PayJs extends AbstractPayment
{
+ private static $config;
+
+ function __construct()
+ {
+ parent::__construct();
+ self::$config = [
+ 'mchid' => self::$systemConfig['payjs_mch_id'], // 配置商户号
+ 'key' => self::$systemConfig['payjs_key'], // 配置通信密钥
+ ];
+ }
public function purchase($request)
{
@@ -20,7 +30,7 @@ class PayJs extends AbstractPayment
$payment->amount = $request->input('amount');
$payment->save();
- $result = (new Pay($this->createGateway()))->native([
+ $result = (new Pay($this::$config))->native([
'body' => parent::$systemConfig['subject_name']? : parent::$systemConfig['website_name'],
'total_fee' => $payment->amount*100,
'out_trade_no' => $payment->sn,
@@ -37,21 +47,14 @@ class PayJs extends AbstractPayment
return Response::json(['status' => 'success', 'data' => $payment->sn, 'message' => '创建订单成功!']);
}
- private function createGateway()
- {
- return $config = [
- 'mchid' => self::$systemConfig['payjs_mch_id'], // 配置商户号
- 'key' => self::$systemConfig['payjs_key'], // 配置通信密钥
- ];
- }
public function notify($request)
{
- $data = (new Pay($this->createGateway()))->notify();
+ $data = (new Pay($this::$config))->notify();
if($data['return_code'] == 1){
- $this->postPayment($data['out_trade_no'], 6);
+ $this::postPayment($data['out_trade_no'], 'PayJs');
exit("success");
}
exit("fail");
diff --git a/app/Http/Controllers/Gateway/PayPal.php b/app/Http/Controllers/Gateway/PayPal.php
new file mode 100644
index 00000000..55868e21
--- /dev/null
+++ b/app/Http/Controllers/Gateway/PayPal.php
@@ -0,0 +1,132 @@
+provider = new ExpressCheckout();
+ $config = [
+ 'mode' => 'live',
+ 'live' => [
+ 'username' => self::$systemConfig['paypal_username'],
+ 'password' => self::$systemConfig['paypal_password'],
+ 'secret' => self::$systemConfig['paypal_secret'],
+ 'certificate' => self::$systemConfig['paypal_certificate'],
+ 'app_id' => self::$systemConfig['paypal_app_id'],
+ ],
+
+ 'payment_action' => 'Sale',
+ 'currency' => env('PAYPAL_CURRENCY', 'USD'),
+ 'billing_type' => 'MerchantInitiatedBilling',
+ 'notify_url' => (self::$systemConfig['website_callback_url']? : self::$systemConfig['website_url']).'/callback/notify?method=paypal',
+ 'locale' => 'zh-CN',
+ 'validate_ssl' => TRUE,
+ ];
+ $this->provider->setApiCredentials($config);
+ }
+
+ public function purchase(Request $request)
+ {
+ $payment = new Payment();
+ $payment->sn = self::generateGuid();
+ $payment->user_id = Auth::user()->id;
+ $payment->oid = $request->input('oid');
+ $payment->amount = $request->input('amount');
+ $payment->save();
+
+ $data = $this->getCheckoutData($payment->sn, $payment->amount);
+
+ try{
+ $response = $this->provider->setExpressCheckout($data);
+
+ return Response::json(['status' => 'success', 'url' => $response['paypal_link'], 'message' => '创建订单成功!']);
+ }catch(Exception $e){
+ Log::error("【PayPal】错误: ".$e->getMessage());
+ exit;
+ }
+ }
+
+ protected function getCheckoutData($sn, $amount)
+ {
+ return [
+ 'invoice_id' => $sn,
+ 'items' => [
+ [
+ 'name' => self::$systemConfig['subject_name']? : self::$systemConfig['website_name'],
+ 'price' => $amount,
+ 'desc' => 'Description for'.(self::$systemConfig['subject_name']? : self::$systemConfig['website_name']),
+ 'qty' => 1
+ ]
+ ],
+ 'invoice_description' => $sn,
+ 'return_url' => self::$systemConfig['website_url'].'/callback/checkout',
+ 'cancel_url' => self::$systemConfig['website_url'].'/invoices',
+ 'total' => $amount,
+ ];
+ }
+
+ public function getCheckout(Request $request)
+ {
+ $token = $request->get('token');
+ $PayerID = $request->get('PayerID');
+
+ // Verify Express Checkout Token
+ $response = $this->provider->getExpressCheckoutDetails($token);
+
+ if(in_array(strtoupper($response['ACK']), ['SUCCESS', 'SUCCESSWITHWARNING'])){
+ $payment = Payment::whereSn($response['INVNUM'])->first();
+ $data = $this->getCheckoutData($payment->sn, $payment->amount);
+ // Perform transaction on PayPal
+ $payment_status = $this->provider->doExpressCheckoutPayment($data, $token, $PayerID);
+ $status = $payment_status['PAYMENTINFO_0_PAYMENTSTATUS'];
+
+ if(!strcasecmp($status, 'Completed') || !strcasecmp($status, 'Processed')){
+ Log::info("Order $payment->id has been paid successfully!");
+ }else{
+ Log::error("Error processing PayPal payment for Order $payment->id!");
+ }
+ }
+
+ return redirect('/invoices');
+ }
+
+ public function notify(Request $request)
+ {
+ $request->merge(['cmd' => '_notify-validate']);
+ $post = $request->all();
+
+ $response = (string)$this->provider->verifyIPN($post);
+
+ if($response === 'VERIFIED' && $request['mp_desc']){
+ if(Payment::whereSn($request['mp_desc'])->first()->status == 0){
+ self::postPayment($request['mp_desc'], 'PayPal');
+ }
+ exit("success");
+ }
+ exit("fail");
+ }
+
+ public function getReturnHTML(Request $request)
+ {
+ // TODO: Implement getReturnHTML() method.
+ }
+
+ public function getPurchaseHTML()
+ {
+ // TODO: Implement getPurchaseHTML() method.
+ }
+}
\ No newline at end of file
diff --git a/app/Http/Controllers/PaymentController.php b/app/Http/Controllers/PaymentController.php
index 56266140..ebb368fc 100644
--- a/app/Http/Controllers/PaymentController.php
+++ b/app/Http/Controllers/PaymentController.php
@@ -8,6 +8,7 @@ use App\Http\Controllers\Gateway\CodePay;
use App\Http\Controllers\Gateway\F2Fpay;
use App\Http\Controllers\Gateway\Local;
use App\Http\Controllers\Gateway\PayJs;
+use App\Http\Controllers\Gateway\PayPal;
use App\Http\Models\Coupon;
use App\Http\Models\Goods;
use App\Http\Models\Order;
@@ -52,6 +53,8 @@ class PaymentController extends Controller
return new PayJs();
case 'bitpayx':
return new BitpayX();
+ case 'paypal':
+ return new PayPal();
default:
Log::error("未知支付:".self::$method);
diff --git a/composer.json b/composer.json
index c9a15f3d..367b336f 100644
--- a/composer.json
+++ b/composer.json
@@ -34,6 +34,7 @@
"riverslei/payment": "*",
"scyllaly/hcaptcha": "^4.1",
"spatie/laravel-permission": "^3.11",
+ "srmklive/paypal": "~1.0",
"xhat/payjs": "^1.4"
},
"require-dev": {
diff --git a/composer.lock b/composer.lock
index 08fd5ecc..7cc0f0f7 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "68f2779fa41d9186d580acfd60389815",
+ "content-hash": "2e58b1a54b626dad916eeb405654defa",
"packages": [
{
"name": "barryvdh/laravel-debugbar",
@@ -4369,6 +4369,67 @@
],
"time": "2020-03-03T21:31:02+00:00"
},
+ {
+ "name": "srmklive/paypal",
+ "version": "1.7.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/srmklive/laravel-paypal.git",
+ "reference": "09b78947e302837eb7851e6a4c204532c9ef4927"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/srmklive/laravel-paypal/zipball/09b78947e302837eb7851e6a4c204532c9ef4927",
+ "reference": "09b78947e302837eb7851e6a4c204532c9ef4927",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "guzzlehttp/guzzle": "~6.0",
+ "illuminate/support": "~5.1|~5.2|~5.3|~5.4|~5.5|~5.6|~5.7|~5.8|~6.0|~7.0",
+ "nesbot/carbon": "~1.0|~2.0"
+ },
+ "type": "library",
+ "extra": {
+ "laravel": {
+ "providers": [
+ "Srmklive\\PayPal\\Providers\\PayPalServiceProvider"
+ ],
+ "aliases": {
+ "PayPal": "Srmklive\\PayPal\\Facades\\PayPal"
+ }
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Srmklive\\PayPal\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Raza Mehdi",
+ "email": "srmk@outlook.com"
+ }
+ ],
+ "description": "Laravel plugin For Processing Payments Through Paypal Express Checkout. Can Be Used Independently With Other Applications.",
+ "keywords": [
+ "http",
+ "laravel paypal",
+ "paypal",
+ "rest",
+ "web service"
+ ],
+ "time": "2020-03-03T09:42:56+00:00"
+ },
{
"name": "swiftmailer/swiftmailer",
"version": "v6.2.3",
diff --git a/config/app.php b/config/app.php
index a5aa7f1b..ba57811f 100644
--- a/config/app.php
+++ b/config/app.php
@@ -158,6 +158,7 @@ return [
Overtrue\LaravelLang\TranslationServiceProvider::class, // 多国语言包功能
Rap2hpoutre\LaravelLogViewer\LaravelLogViewerServiceProvider::class,//日志查看
Scyllaly\HCaptcha\HCaptchaServiceProvider::class, //HCaptcha
+ Srmklive\PayPal\Providers\PayPalServiceProvider::class, // PayPal
/*
* Application Service Providers...
@@ -182,29 +183,35 @@ return [
*/
'aliases' => [
+ 'Agent' => Jenssegers\Agent\Facades\Agent::class,
'App' => Illuminate\Support\Facades\App::class,
'Artisan' => Illuminate\Support\Facades\Artisan::class,
'Auth' => Illuminate\Support\Facades\Auth::class,
'Blade' => Illuminate\Support\Facades\Blade::class,
'Broadcast' => Illuminate\Support\Facades\Broadcast::class,
- 'Debugbar' => Barryvdh\Debugbar\Facade::class,
'Bus' => Illuminate\Support\Facades\Bus::class,
'Cache' => Illuminate\Support\Facades\Cache::class,
+ 'Captcha' => Mews\Captcha\Facades\Captcha::class,
'Config' => Illuminate\Support\Facades\Config::class,
'Cookie' => Illuminate\Support\Facades\Cookie::class,
'Crypt' => Illuminate\Support\Facades\Crypt::class,
'DB' => Illuminate\Support\Facades\DB::class,
+ 'Debugbar' => Barryvdh\Debugbar\Facade::class,
'Eloquent' => Illuminate\Database\Eloquent\Model::class,
'Event' => Illuminate\Support\Facades\Event::class,
'File' => Illuminate\Support\Facades\File::class,
'Gate' => Illuminate\Support\Facades\Gate::class,
+ 'Geetest' => Misechow\Geetest\Geetest::class,
'Hash' => Illuminate\Support\Facades\Hash::class,
'HCaptcha' => Scyllaly\HCaptcha\Facades\HCaptcha::class,
'Lang' => Illuminate\Support\Facades\Lang::class,
'Log' => Illuminate\Support\Facades\Log::class,
'Mail' => Illuminate\Support\Facades\Mail::class,
+ 'NoCaptcha' => Misechow\NoCaptcha\Facades\NoCaptcha::class,
'Notification' => Illuminate\Support\Facades\Notification::class,
'Password' => Illuminate\Support\Facades\Password::class,
+ 'PayPal' => Srmklive\PayPal\Facades\PayPal::class,
+ 'Purifier' => Mews\Purifier\Facades\Purifier::class,
'Queue' => Illuminate\Support\Facades\Queue::class,
'Redirect' => Illuminate\Support\Facades\Redirect::class,
'Redis' => Illuminate\Support\Facades\Redis::class,
@@ -217,11 +224,6 @@ return [
'URL' => Illuminate\Support\Facades\URL::class,
'Validator' => Illuminate\Support\Facades\Validator::class,
'View' => Illuminate\Support\Facades\View::class,
- 'Captcha' => Mews\Captcha\Facades\Captcha::class,
- 'Agent' => Jenssegers\Agent\Facades\Agent::class,
- 'Purifier' => Mews\Purifier\Facades\Purifier::class,
- 'Geetest' => Misechow\Geetest\Geetest::class,
- 'NoCaptcha' => Misechow\NoCaptcha\Facades\NoCaptcha::class,
],
];
diff --git a/config/paypal.php b/config/paypal.php
new file mode 100644
index 00000000..8c3d7e92
--- /dev/null
+++ b/config/paypal.php
@@ -0,0 +1,30 @@
+.
+ */
+
+return [
+ 'mode' => env('PAYPAL_MODE', 'sandbox'), // Can only be 'sandbox' Or 'live'. If empty or invalid, 'live' will be used.
+ 'sandbox' => [
+ 'username' => env('PAYPAL_SANDBOX_API_USERNAME', ''),
+ 'password' => env('PAYPAL_SANDBOX_API_PASSWORD', ''),
+ 'secret' => env('PAYPAL_SANDBOX_API_SECRET', ''),
+ 'certificate' => env('PAYPAL_SANDBOX_API_CERTIFICATE', ''),
+ 'app_id' => 'APP-80W284485P519543T', // Used for testing Adaptive Payments API in sandbox mode
+ ],
+ 'live' => [
+ 'username' => env('PAYPAL_LIVE_API_USERNAME', ''),
+ 'password' => env('PAYPAL_LIVE_API_PASSWORD', ''),
+ 'secret' => env('PAYPAL_LIVE_API_SECRET', ''),
+ 'certificate' => env('PAYPAL_LIVE_API_CERTIFICATE', ''),
+ 'app_id' => '', // Used for Adaptive Payments API
+ ],
+
+ 'payment_action' => 'Sale', // Can only be 'Sale', 'Authorization' or 'Order'
+ 'currency' => env('PAYPAL_CURRENCY', 'USD'),
+ 'billing_type' => 'MerchantInitiatedBilling',
+ 'notify_url' => '', // Change this accordingly for your application.
+ 'locale' => 'zh-CN', // force gateway language i.e. it_IT, es_ES, en_US ... (for express checkout only)
+ 'validate_ssl' => true, // Validate SSL when creating api client.
+];
diff --git a/resources/views/admin/layouts.blade.php b/resources/views/admin/layouts.blade.php
index 38cc9db8..6ecef7f3 100644
--- a/resources/views/admin/layouts.blade.php
+++ b/resources/views/admin/layouts.blade.php
@@ -80,7 +80,7 @@
-
+
diff --git a/resources/views/admin/system.blade.php b/resources/views/admin/system.blade.php
index 12350903..d06a1d97 100644
--- a/resources/views/admin/system.blade.php
+++ b/resources/views/admin/system.blade.php
@@ -1023,6 +1023,7 @@
@@ -1194,6 +1195,71 @@
+
diff --git a/resources/views/admin/ticket/replyTicket.blade.php b/resources/views/admin/ticket/replyTicket.blade.php
index 101b4c1d..b2429d8e 100644
--- a/resources/views/admin/ticket/replyTicket.blade.php
+++ b/resources/views/admin/ticket/replyTicket.blade.php
@@ -25,7 +25,7 @@
@elseif(strpos(strtolower($ticket->user->email),"@qq.com") !== FALSE)
@else
-
+
@endif
@@ -52,7 +52,7 @@
@elseif(strpos(strtolower($ticket->user->email),"@qq.com") !== FALSE)
@else
-
+
@endif
@endif
diff --git a/resources/views/user/components/purchase.blade.php b/resources/views/user/components/purchase.blade.php
index f39c1063..15342879 100644
--- a/resources/views/user/components/purchase.blade.php
+++ b/resources/views/user/components/purchase.blade.php
@@ -7,6 +7,8 @@
@if(\App\Components\Helpers::systemConfig()['is_WeChatPay'])
'
@endif
-@if(\App\Components\Helpers::systemConfig()['is_otherPay'])
- '
+@if(\App\Components\Helpers::systemConfig()['is_otherPay'] == 'bitpayx')
+ '
+@elseif(\App\Components\Helpers::systemConfig()['is_otherPay'] == 'paypal')
+ '
@endif
\ No newline at end of file
diff --git a/routes/web.php b/routes/web.php
index 4e39cac5..29e01af0 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -176,4 +176,7 @@ Route::group(['middleware' => ['isForbidden', 'isMaintenance', 'isLogin']], func
});
});
-Route::post('callback/notify', 'PaymentController@notify'); //支付回调
+Route::group(['prefix' => 'callback'], function(){
+ Route::get('checkout', 'Gateway\PayPal@getCheckout');
+ Route::post('notify', 'PaymentController@notify'); //支付回调
+});
diff --git a/sql/mod/20200426.sql b/sql/mod/20200426.sql
new file mode 100644
index 00000000..ec5dcb14
--- /dev/null
+++ b/sql/mod/20200426.sql
@@ -0,0 +1,6 @@
+INSERT INTO `config` VALUES ('107', 'paypal_username', '');
+INSERT INTO `config` VALUES ('108', 'paypal_password', '');
+INSERT INTO `config` VALUES ('109', 'paypal_secret', '');
+INSERT INTO `config` VALUES ('110', 'paypal_certificate', '');
+INSERT INTO `config` VALUES ('111', 'paypal_app_id', '');
+