364 lines
13 KiB
PHP
364 lines
13 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Enums\PaymentMethod;
|
|
use App\Enums\PaymentTransactionStatus;
|
|
use App\Models\PaymentAggregator;
|
|
use App\Models\PaymentTransaction;
|
|
use GuzzleHttp\Client;
|
|
use GuzzleHttp\HandlerStack;
|
|
use GuzzleHttp\MessageFormatter;
|
|
use GuzzleHttp\Middleware;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Carbon;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Lang;
|
|
use Illuminate\Support\Facades\Log;
|
|
use Illuminate\Support\Str;
|
|
use Throwable;
|
|
|
|
class YnoteOrangeController extends Controller
|
|
{
|
|
private $client;
|
|
private $timeout = 60; //In seconds
|
|
private $isSyncRequest = false ;// Async request
|
|
|
|
private $username = 'iJ7hLnkD8sYddWGV2ZzT7bbsKQMa';
|
|
private $password = 'gglK1uIj8WGyVUcCEimoI_Cm0o4a';
|
|
private $channelUserMsisdn = '659923684';
|
|
private $PIN = '1468';
|
|
private $X_AUTH_TOKEN = 'WU5PVEVIRUFEOllOT1RFSEVBRDIwMjA=';
|
|
|
|
private $baseURL = 'https://api-s1.orange.cm';
|
|
|
|
/**
|
|
* Create a new controller instance.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function __construct()
|
|
{
|
|
// Create a client with a base URI
|
|
$this->client = new Client([
|
|
'base_uri' => $this->baseURL,
|
|
'timeout' => $this->timeout,
|
|
'http_errors' => false,
|
|
'verify' => false, // Disable SSL Certification verification
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* @OA\Get(
|
|
* path="/ynote/methods",
|
|
* summary="Afficher la liste des methodes de paiment de Ynote",
|
|
* tags={"Ynote"},
|
|
* security={{"api_key":{}}},
|
|
* @OA\Response(
|
|
* response=200,
|
|
* description="OK",
|
|
* @OA\JsonContent(
|
|
* ref="#/components/schemas/ApiResponse",
|
|
* example = {
|
|
* "status" : 200,
|
|
* "response" : {"hasWebview": true, "methods": {"Orange": "Orange"}},
|
|
* "error":null
|
|
* }
|
|
* )
|
|
* )
|
|
* )
|
|
*/
|
|
public function getMethods()
|
|
{
|
|
return $this->successResponse([
|
|
'hasWebview' => true,
|
|
'methods' => [
|
|
'Orange' => 'Orange'
|
|
]
|
|
]
|
|
);
|
|
}
|
|
|
|
|
|
/*
|
|
* Init payment and provide iLink World checkout page to handle payment
|
|
*/
|
|
public function initPay(Request $request)
|
|
{
|
|
$this->validate($request, [
|
|
// 'aggregator_id' => 'required|integer|exists:payment_aggregators,id',
|
|
'amount' => 'required|numeric|min:5',
|
|
'currency' => 'required|string|size:3',
|
|
'customer_id' => 'required|integer',
|
|
'customer_email' => 'required|email',
|
|
'customer_name' => 'nullable|string',
|
|
'customer_surname' => 'required|string',
|
|
'customer_phone_number' => 'required|string',
|
|
'customer_address' => 'required|string',
|
|
'customer_country' => 'required|string|size:2',
|
|
'reason' => 'required|string',
|
|
]);
|
|
|
|
$aggregator = PaymentAggregator::where('name','like','%ynote%')->firstOrFail();
|
|
|
|
$transaction_id = $this->getTransactionID();
|
|
$payment_method = 'Orange';
|
|
$customer_phone_number = $request->input('customer_phone_number');
|
|
if(str_contains($customer_phone_number,'+237')){
|
|
$customer_phone_number = substr($customer_phone_number,4);
|
|
}
|
|
|
|
do{
|
|
$payment_token = Str::random(64);
|
|
}while(PaymentTransaction::where('payment_token', $payment_token)->exists());
|
|
|
|
|
|
$payment_url = route('checkout',['payment_token' => $payment_token]);
|
|
|
|
PaymentTransaction::create([
|
|
'aggregator_id' => $aggregator->id,
|
|
"currency" => $request->input('currency'),
|
|
"transaction_id" => $transaction_id,
|
|
"amount" => $request->input('amount'),
|
|
"payment_method" => $payment_method,
|
|
"payment_url" => $payment_url,
|
|
'payment_token' => $payment_token,
|
|
'status' => PaymentTransactionStatus::INITIATED,
|
|
"reason" => $request->input('reason'),
|
|
"customer_id" => $request->input('customer_id'),
|
|
"customer_name" => $request->input('customer_name'),
|
|
"customer_surname" => $request->input('customer_surname'),
|
|
"customer_email" => $request->input('customer_email'),
|
|
"customer_phone_number" => $customer_phone_number,
|
|
"customer_address" => $request->input('customer_address'),
|
|
"customer_city" => $request->input('customer_city'),
|
|
"customer_country" => $request->input('customer_country'),
|
|
"customer_state" => $request->input('customer_state'),
|
|
"customer_zip_code" => $request->input('customer_zip_code'),
|
|
"payment_endpoint" => route('ynote.checkoutPay'),
|
|
"payment_verify_endpoint" => route('ynote.status'),
|
|
]);
|
|
|
|
return $this->successResponse([
|
|
'message' => 'Payment initiated',
|
|
'payment_url' => $payment_url
|
|
]);
|
|
}
|
|
|
|
public function checkoutPay(Request $request)
|
|
{
|
|
$this->validate($request, [
|
|
'payment_token' => 'required|string|exists:payment_transactions,payment_token',
|
|
'phone_number' => 'required|string',
|
|
]);
|
|
|
|
$token = $request->input('payment_token');
|
|
$transaction = PaymentTransaction::where('payment_token', $token)
|
|
->where('status', PaymentTransactionStatus::INITIATED)->firstOrFail();
|
|
|
|
|
|
$customer_phone_number = $request->input('phone_number');
|
|
if(str_contains($customer_phone_number,'+237')){
|
|
$customer_phone_number = substr($customer_phone_number,4);
|
|
}
|
|
|
|
|
|
// Get Access Token
|
|
$accessTokenResponse = $this->client->post('/token', [
|
|
'headers' => [
|
|
'Authorization' => 'Basic '.base64_encode($this->username.':'.$this->password),
|
|
],
|
|
'form_params' => [
|
|
'grant_type' => 'client_credentials',
|
|
]
|
|
]);
|
|
|
|
|
|
$responseBody = json_decode($accessTokenResponse->getBody()->getContents());
|
|
|
|
if ($accessTokenResponse->getStatusCode() == 200) {
|
|
|
|
$accessToken = $responseBody->access_token;
|
|
|
|
// Init Payment
|
|
$initResponse = $this->client->post('/omcoreapis/1.0.2/mp/init', [
|
|
'headers' => [
|
|
'Authorization' => 'Bearer '.$accessToken,
|
|
'X-AUTH-TOKEN' => $this->X_AUTH_TOKEN
|
|
],
|
|
]);
|
|
|
|
|
|
$responseBody = json_decode($initResponse->getBody()->getContents());
|
|
|
|
if ($initResponse->getStatusCode() == 200) {
|
|
|
|
$payToken = $responseBody->data->payToken;
|
|
|
|
// Make Payment
|
|
$makeResponse = $this->client->post('/omcoreapis/1.0.2/mp/pay', [
|
|
'headers' => [
|
|
'Authorization' => 'Bearer ' . $accessToken,
|
|
'X-AUTH-TOKEN' => $this->X_AUTH_TOKEN
|
|
],
|
|
'json' => [
|
|
'notifUrl' => 'http://28f8-102-244-222-34.ngrok-free.app/ynote/webhook' ,// route('ynote.webhook'),
|
|
'channelUserMsisdn' => $this->channelUserMsisdn,
|
|
'pin' => $this->PIN,
|
|
'amount' => ''.intval(ceil(floatval($transaction->amount))),
|
|
'subscriberMsisdn' => $customer_phone_number,
|
|
'orderId' => $transaction->transaction_id,
|
|
'description' => $transaction->reason,
|
|
'payToken' => $payToken
|
|
]
|
|
]);
|
|
|
|
$responseBody = json_decode($makeResponse->getBody()->getContents());
|
|
if ($makeResponse->getStatusCode() == 200) {
|
|
|
|
$transaction->update([
|
|
'aggregator_payment_ref' => $responseBody->data->txnid,
|
|
'aggregator_payment_token' => $payToken,
|
|
'status' => strtoupper($responseBody->data->status),
|
|
"customer_phone_number" => $customer_phone_number,
|
|
]);
|
|
|
|
return redirect()->route('checkout',['payment_token' => $token]);
|
|
|
|
|
|
}else{
|
|
//If error regenerate transaction id to avoid 'Numero de commande deja utilisé'
|
|
$transaction->update([
|
|
'transaction_id' => $this->getTransactionID()
|
|
]);
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!empty($responseBody->error)){
|
|
//Convert into single line
|
|
session()->flash('error',str_replace(array("\n", "\r"), '',$responseBody->error_description));
|
|
return redirect()->route('checkout',['payment_token' => $token]);
|
|
}
|
|
|
|
Log::error(json_encode($responseBody));
|
|
session()->flash('error',__('errors.unexpected_error'));
|
|
return redirect()->route('checkout',['payment_token' => $token]);
|
|
}
|
|
|
|
public function capturePaymentResult(Request $request)
|
|
{
|
|
Log::info("capture");
|
|
Log::warning(json_encode($request->all()));
|
|
|
|
$this->validate($request, [
|
|
'txnid' => 'required|string'
|
|
]);
|
|
|
|
$transaction = PaymentTransaction::where('aggregator_payment_ref',$request->input('txnid'))->first();
|
|
return $this->getPaymentStatus($transaction);
|
|
}
|
|
|
|
public function getPaymentStatus(Request $request)
|
|
{
|
|
$this->validate($request, [
|
|
'transaction_id' => 'required|string|exists:payment_transactions,transaction_id',
|
|
'verify_btn' => 'nullable|boolean'
|
|
]);
|
|
|
|
$transaction = PaymentTransaction::where('transaction_id',$request->input('transaction_id'))->first();
|
|
$verify_btn = $request->input('verify_btn');
|
|
|
|
try {
|
|
|
|
// Si le paiement fait plus de 5 min on l'annule
|
|
if($transaction->status == PaymentTransactionStatus::PENDING && $transaction->created_at->diffInMinutes(Carbon::now()) > 5){
|
|
$transaction->update([
|
|
'status' => PaymentTransactionStatus::CANCELLED
|
|
]);
|
|
}
|
|
|
|
// Get Access Token
|
|
$accessTokenResponse = $this->client->post('/token', [
|
|
'headers' => [
|
|
'Authorization' => 'Basic '.base64_encode($this->username.':'.$this->password),
|
|
],
|
|
'form_params' => [
|
|
'grant_type' => 'client_credentials',
|
|
]
|
|
]);
|
|
|
|
$responseBody = json_decode($accessTokenResponse->getBody()->getContents());
|
|
|
|
if ($accessTokenResponse->getStatusCode() == 200) {
|
|
|
|
$accessToken = $responseBody->access_token;
|
|
|
|
$response = $this->client->get('/omcoreapis/1.0.2/mp/paymentstatus/'.$transaction->aggregator_payment_token, [
|
|
'headers' => [
|
|
'Authorization' => 'Bearer '.$accessToken,
|
|
'X-AUTH-TOKEN' => $this->X_AUTH_TOKEN
|
|
],
|
|
]);
|
|
|
|
$responseData = json_decode($response->getBody()->getContents());
|
|
$responseCode = $response->getStatusCode();
|
|
|
|
Log::info(json_encode($responseData));
|
|
Log::info(json_encode($responseCode));
|
|
Log::info(json_encode([
|
|
'Authorization' => 'Bearer '.$accessToken,
|
|
'X-AUTH-TOKEN' => $this->X_AUTH_TOKEN
|
|
]));
|
|
|
|
Log::info(json_encode($transaction->aggregator_payment_token));
|
|
|
|
Log::info("Veriication de paement");
|
|
if ($responseCode == 200) {
|
|
|
|
$status = strtoupper($responseData->data->status);
|
|
if(str_contains($status,'SUCCESS')){
|
|
$status = PaymentTransactionStatus::ACCEPTED;
|
|
}
|
|
|
|
|
|
$transaction->update([
|
|
'status' => $status,
|
|
'payment_date' => $responseData->data->transaction_date ?? null,
|
|
]);
|
|
}
|
|
}
|
|
|
|
|
|
} catch (Throwable $e) {
|
|
Log::info("Get Ynote Payment Status Error");
|
|
Log::info($e);
|
|
$transaction->update([
|
|
'status' => PaymentTransactionStatus::REFUSED
|
|
]);
|
|
|
|
}
|
|
|
|
if($verify_btn){
|
|
return redirect()->route('checkout',['payment_token' => $transaction->payment_token]);
|
|
}else {
|
|
Log::warning($transaction->status);
|
|
if ($transaction->status == PaymentTransactionStatus::ACCEPTED) {
|
|
return [
|
|
'message' => "Payment accepted",
|
|
'status' => 1,
|
|
'refresh' => true,
|
|
];
|
|
} else {
|
|
return [
|
|
'message' => "Payment failed",
|
|
'status' => 0,
|
|
'refresh' => $transaction->status == PaymentTransactionStatus::PENDING
|
|
];
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
}
|