Jelajahi Sumber

添加 PayPal 支付方式

兔姬桑 4 tahun lalu
induk
melakukan
80841248e5

+ 0 - 5
.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=

+ 5 - 0
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;

+ 13 - 10
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");

+ 132 - 0
app/Http/Controllers/Gateway/PayPal.php

@@ -0,0 +1,132 @@
+<?php
+
+
+namespace App\Http\Controllers\Gateway;
+
+use App\Http\Models\Payment;
+use Auth;
+use Exception;
+use Illuminate\Http\Request;
+use Log;
+use Response;
+use Srmklive\PayPal\Services\ExpressCheckout;
+
+class PayPal extends AbstractPayment
+{
+	protected $provider;
+
+	public function __construct()
+	{
+		parent::__construct();
+		$this->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.
+	}
+}

+ 3 - 0
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);
 

+ 1 - 0
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": {

+ 62 - 1
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",

+ 8 - 6
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,
 	],
 
 ];

+ 30 - 0
config/paypal.php

@@ -0,0 +1,30 @@
+<?php
+/**
+ * PayPal Setting & API Credentials
+ * Created by Raza Mehdi <srmk@outlook.com>.
+ */
+
+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.
+];

+ 1 - 1
resources/views/admin/layouts.blade.php

@@ -80,7 +80,7 @@
 				<li class="nav-item dropdown">
 					<a class="nav-link navbar-avatar" data-toggle="dropdown" href="#" aria-expanded="false" data-animation="scale-up" role="button">
                 <span class="avatar avatar-online">
-                  <img src="/assets/images/astronaut.svg" alt="..."/>
+                  <img src="/assets/images/avatar.svg" alt="..."/>
                     <i></i>
                 </span>
 					</a>

+ 66 - 0
resources/views/admin/system.blade.php

@@ -1023,6 +1023,7 @@
 											<select class="col-md-3" id="is_otherPay" data-plugin="selectpicker" data-style="btn-outline btn-primary" onchange="updateFromOther('select','is_otherPay')">
 												<option value="">关闭</option>
 												<option value="bitpayx">麻瓜宝</option>
+												<option value="paypal">PayPal</option>
 											</select>
 										</div>
 									</div>
@@ -1194,6 +1195,71 @@
 										</div>
 									</div>
 								</div>
+								<div class="row pb-70">
+									<div class="form-group col-lg-6">
+										<div class="row">
+											<label class="col-md-3 col-form-label">PayPal</label>
+											<div class="col-md-7">
+												请到 <a href="" target="_blank">PayPal</a> 申请账号
+											</div>
+										</div>
+									</div>
+									<div class="form-group col-lg-6">
+										<div class="row">
+											<label class="col-md-3 col-form-label" for="paypal_username">账号</label>
+											<div class="col-md-7">
+												<div class="input-group">
+													<input type="text" class="form-control" id="paypal_username" value="{{$paypal_username}}"/>
+													<span class="input-group-append"><button class="btn btn-primary" type="button" onclick="update('paypal_username')">修改</button></span>
+												</div>
+											</div>
+										</div>
+									</div>
+									<div class="form-group col-lg-6">
+										<div class="row">
+											<label class="col-md-3 col-form-label" for="paypal_password">密码</label>
+											<div class="col-md-7">
+												<div class="input-group">
+													<input type="text" class="form-control" id="paypal_password" value="{{$paypal_password}}"/>
+													<span class="input-group-append"><button class="btn btn-primary" type="button" onclick="update('paypal_password')">修改</button></span>
+												</div>
+											</div>
+										</div>
+									</div>
+									<div class="form-group col-lg-6">
+										<div class="row">
+											<label class="col-md-3 col-form-label" for="paypal_secret">秘钥</label>
+											<div class="col-md-7">
+												<div class="input-group">
+													<input type="text" class="form-control" id="paypal_secret" value="{{$paypal_secret}}"/>
+													<span class="input-group-append"><button class="btn btn-primary" type="button" onclick="update('paypal_secret')">修改</button></span>
+												</div>
+											</div>
+										</div>
+									</div>
+									<div class="form-group col-lg-6">
+										<div class="row">
+											<label class="col-md-3 col-form-label" for="paypal_certificate">证书</label>
+											<div class="col-md-7">
+												<div class="input-group">
+													<input type="text" class="form-control" id="paypal_certificate" value="{{$paypal_certificate}}"/>
+													<span class="input-group-append"><button class="btn btn-primary" type="button" onclick="update('paypal_certificate')">修改</button></span>
+												</div>
+											</div>
+										</div>
+									</div>
+									<div class="form-group col-lg-6">
+										<div class="row">
+											<label class="col-md-3 col-form-label" for="paypal_app_id">应用ID</label>
+											<div class="col-md-7">
+												<div class="input-group">
+													<input type="text" class="form-control" id="paypal_app_id" value="{{$paypal_app_id}}"/>
+													<span class="input-group-append"><button class="btn btn-primary" type="button" onclick="update('paypal_app_id')">修改</button></span>
+												</div>
+											</div>
+										</div>
+									</div>
+								</div>
 							</form>
 						</div>
 					</div>

+ 2 - 2
resources/views/admin/ticket/replyTicket.blade.php

@@ -25,7 +25,7 @@
 									@elseif(strpos(strtolower($ticket->user->email),"@qq.com") !== FALSE)
 										<img src="http://q1.qlogo.cn/g?b=qq&nk={{$ticket->user->email}}&s=640" alt="客户">
 									@else
-										<img src="/assets/images/astronaut.svg" alt="客户">
+										<img src="/assets/images/avatar.svg" alt="客户">
 									@endif
 								</p>
 							</div>
@@ -52,7 +52,7 @@
 											@elseif(strpos(strtolower($ticket->user->email),"@qq.com") !== FALSE)
 												<img src="http://q1.qlogo.cn/g?b=qq&nk={{$ticket->user->email}}&s=640" alt="客户">
 											@else
-												<img src="/assets/images/astronaut.svg" alt="客户">
+												<img src="/assets/images/avatar.svg" alt="客户">
 											@endif
 										</p>
 									@endif

+ 4 - 2
resources/views/user/components/purchase.blade.php

@@ -7,6 +7,8 @@
 @if(\App\Components\Helpers::systemConfig()['is_WeChatPay'])
 	<button class="btn btn-flat waves-attach" onclick="pay('{{\App\Components\Helpers::systemConfig()['is_WeChatPay']}}','3')"><img src="/assets/images/wechat.svg" width="50px" height="50px" alt="wechat"/></button>'
 @endif
-@if(\App\Components\Helpers::systemConfig()['is_otherPay'])
-	<button class="btn btn-flat waves-attach" onclick="pay('{{\App\Components\Helpers::systemConfig()['is_otherPay']}}','x')"><img src="/assets/images/bitcoin.svg" width="50px" height="50px" alt="other"/></button>'
+@if(\App\Components\Helpers::systemConfig()['is_otherPay'] == 'bitpayx')
+	<button class="btn btn-flat waves-attach" onclick="pay('{{\App\Components\Helpers::systemConfig()['is_otherPay']}}','')"><img src="/assets/images/bitcoin.svg" width="50px" height="50px" alt="other"/></button>'
+@elseif(\App\Components\Helpers::systemConfig()['is_otherPay'] == 'paypal')
+	<button class="btn btn-flat waves-attach" onclick="pay('{{\App\Components\Helpers::systemConfig()['is_otherPay']}}','')"><img src="/assets/images/paypal.svg" width="50px" alt="other"/></button>'
 @endif

+ 4 - 1
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'); //支付回调
+});

+ 6 - 0
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', '');
+