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 @@