Created
July 26, 2024 15:10
-
-
Save bulentsakarya/a76a4333704bcb0be811f17bb308adc2 to your computer and use it in GitHub Desktop.
Laravel Paytr Ödeme Entegrasyonu
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
return [ | |
'merchant_id' => env('PAYTR_MERCHANT_ID', "PAYTR_MERCHANT_ID"), | |
'merchant_key' => env('PAYTR_MERCHANT_KEY', "PAYTR_MERCHANT_KEY"), | |
'merchant_salt' => env('PAYTR_MERCHANT_SALT', "PAYTR_MERCHANT_SALT"), | |
'max_installment' => 0, | |
'no_installment' => 0, | |
'test_mode' => 1, | |
'debug_on' => 1, | |
'non_3d' => 0, | |
'lang' => 'tr', | |
'merchant_ok_url' => env('PAYTR_OK_URL', "PAYTR_OK_URL"), | |
'merchant_fail_url' => env('PAYTR_FAIL_URL', "PAYTR_FAIL_URL"), | |
'timeout_limit' => 30, | |
'currency' => 'TRY', | |
'api_type' => 2, | |
'api_url' => env('PAYTR_API_URL', "PAYTR_API_URL"), | |
]; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\Http\Controllers\User\Payment; | |
use App\Http\Controllers\Controller; | |
use App\Models\Service; | |
use App\Models\Order; | |
use App\Services\User\PaymentService; | |
use Illuminate\Http\Request; | |
class PaymentController extends Controller | |
{ | |
protected $paymentService; | |
public function __construct(PaymentService $paymentService) | |
{ | |
$this->paymentService = $paymentService; | |
} | |
public function showPaymentPage(Request $request, Service $service) | |
{ | |
$user = auth()->user(); | |
$invoiceDetails = [ | |
'company_name' => $request->input('company_name'), | |
'address' => $request->input('company_address'), | |
'phone' => $request->input('company_phone'), | |
]; | |
$iframeUrl = $this->paymentService->initiatePayment($service, $invoiceDetails); | |
return view('user.payment.new', compact('iframeUrl', 'service')); | |
} | |
public function paymentCallback(Request $request) | |
{ | |
$result = $this->paymentService->handleCallback($request); | |
if (!$result) { | |
return response('PAYTR notification failed: bad hash', 400); | |
} | |
return response('OK'); | |
} | |
public function paymentSuccess(Request $request) | |
{ | |
$order = Order::where('order_id', $request->merchant_oid)->firstOrFail(); | |
if ($order->status !== 'completed') { | |
return view('payment.waiting', ['order' => $order]); | |
} | |
return redirect()->route('app.services') | |
->with('success', 'Ödemeniz başarıyla gerçekleşti ve servisiniz etkinleştirildi.'); | |
} | |
public function paymentFail(Request $request) | |
{ | |
$order = Order::where('order_id', $request->merchant_oid)->firstOrFail(); | |
if ($order->status !== 'failed') { | |
$order->update(['status' => 'failed']); | |
} | |
return redirect()->route('app.services') | |
->with('error', 'Ödeme işlemi başarısız oldu. Lütfen daha sonra tekrar deneyiniz.'); | |
} | |
public function checkPaymentStatus(Order $order) | |
{ | |
return response()->json(['status' => $order->status]); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\Services\User; | |
use App\Models\Order; | |
use App\Models\Service; | |
use Carbon\Carbon; | |
class PaymentService | |
{ | |
public function initiatePayment(Service $service, array $invoiceDetails) | |
{ | |
$user = auth()->user(); | |
$orderData = $this->prepareOrderData($service, $user, $invoiceDetails); | |
$postVals = $this->preparePostVals($orderData); | |
$result = $this->sendPaytrRequest($postVals); | |
if ($result['status'] == 'success') { | |
$this->createOrder($user, $service, $orderData, $result['token']); | |
return 'https://www.paytr.com/odeme/guvenli/' . $result['token']; | |
} else { | |
throw new \Exception('PAYTR IFRAME failed. reason: ' . $result['reason']); | |
} | |
} | |
protected function prepareOrderData($service, $user, $invoiceDetails) | |
{ | |
return [ | |
'merchant_id' => config('paytr.merchant_id'), | |
'user_ip' => app('request')->ip(), | |
'merchant_oid' => uniqid(), | |
'email' => $invoiceDetails['email_address'] ?? $user->email, | |
'payment_amount' => $service->price * 100, | |
'user_basket' => base64_encode(json_encode([[$service->title, $service->price, 1]])), | |
'user_name' => $invoiceDetails['company_name'] ?? "{$user->name} {$user->surname}", | |
'user_address' => $invoiceDetails['address'] ?? 'Adres bilgisi yok', | |
'user_phone' => $invoiceDetails['phone'] ?? 'Telefon bilgisi yok', | |
]; | |
} | |
protected function preparePostVals($orderData) | |
{ | |
$hashStr = $orderData['merchant_id'] . $orderData['user_ip'] . $orderData['merchant_oid'] . $orderData['email'] . $orderData['payment_amount'] . $orderData['user_basket'] . config('paytr.no_installment') . config('paytr.max_installment') . config('paytr.currency') . config('paytr.test_mode') . config('paytr.merchant_salt'); | |
$token = base64_encode(hash_hmac('sha256', $hashStr, config('paytr.merchant_key'), true)); | |
return array_merge($orderData, [ | |
'paytr_token' => $token, | |
'debug_on' => config('paytr.debug_on'), | |
'no_installment' => config('paytr.no_installment'), | |
'max_installment' => config('paytr.max_installment'), | |
'merchant_ok_url' => config('paytr.merchant_ok_url'), | |
'merchant_fail_url' => config('paytr.merchant_fail_url'), | |
'timeout_limit' => config('paytr.timeout_limit'), | |
'currency' => config('paytr.currency'), | |
'test_mode' => config('paytr.test_mode') | |
]); | |
} | |
protected function sendPaytrRequest($postVals) | |
{ | |
$ch = curl_init(); | |
curl_setopt($ch, CURLOPT_URL, "https://www.paytr.com/odeme/api/get-token"); | |
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); | |
curl_setopt($ch, CURLOPT_POST, 1); | |
curl_setopt($ch, CURLOPT_POSTFIELDS, $postVals); | |
curl_setopt($ch, CURLOPT_FRESH_CONNECT, true); | |
curl_setopt($ch, CURLOPT_TIMEOUT, 20); | |
$result = @curl_exec($ch); | |
if (curl_errno($ch)) { | |
throw new \Exception("PAYTR IFRAME connection error. err:" . curl_error($ch)); | |
} | |
curl_close($ch); | |
return json_decode($result, true); | |
} | |
protected function createOrder($user, $service, $orderData, $token) | |
{ | |
Order::create([ | |
'user_id' => $user->id, | |
'service_id' => $service->id, | |
'order_id' => $orderData['merchant_oid'], | |
'amount' => $service->price, | |
'status' => 'pending', | |
'payment_token' => $token, | |
]); | |
} | |
public function handleCallback($request) | |
{ | |
if (!$this->validateCallback($request)) { | |
return false; | |
} | |
$order = Order::where('order_id', $request->merchant_oid)->firstOrFail(); | |
$request->status == 'success' | |
? $this->processSuccessfulPayment($order) | |
: $this->processFailedPayment($order); | |
return true; | |
} | |
protected function validateCallback($request) | |
{ | |
$merchant_key = config('paytr.merchant_key'); | |
$merchant_salt = config('paytr.merchant_salt'); | |
$hash = base64_encode(hash_hmac('sha256', $request->merchant_oid . $merchant_salt . $request->status . $request->total_amount, $merchant_key, true)); | |
return $hash === $request->hash; | |
} | |
protected function processSuccessfulPayment(Order $order) | |
{ | |
$order->update(['status' => 'completed']); | |
$user = $order->user; | |
$service = $order->service; | |
$activatedAt = Carbon::now(); | |
$user->services()->attach($service->id, [ | |
'is_active' => true, | |
'activated_at' => $activatedAt, | |
'expires_at' => $activatedAt->copy()->addYear(), | |
]); | |
} | |
protected function processFailedPayment(Order $order) | |
{ | |
$order->update(['status' => 'failed']); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment