Skip to content

Commit

Permalink
Merge pull request #379 from Goldengide/fix/payment-callbacks
Browse files Browse the repository at this point in the history
Fix: payment callbacks
  • Loading branch information
Dev-Tonia authored Aug 6, 2024
2 parents 2acc58f + 73d689b commit 81e0e42
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 15 deletions.
53 changes: 47 additions & 6 deletions app/Http/Controllers/Api/V1/PaymentController.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Illuminate\Support\Facades\Validator;
use App\Models\Payment;
use App\Services\PaymentService;
use App\Models\Organisation;
use App\Models\SubscriptionPlan;
use App\Models\UserSubscription;
use Illuminate\Support\Str;
Expand All @@ -26,7 +27,7 @@ public function initiatePaymentForPayStack(Request $request)
{
// return response()->json(['h'=> 'ng']);
$validator = Validator::make($request->all(), [
// 'organisation_id' => 'required',
'organisation_id' => 'required',
'plan_id' =>'required',
'billing_option' => 'required|in:monthly,yearly',
'full_name' => 'required',
Expand All @@ -39,14 +40,31 @@ public function initiatePaymentForPayStack(Request $request)
'message' => 'Validation error: ' . $validator->errors()->first()
], 400);
}
$userIsAnAdminInOrganisation = Organisation::where('user_id', auth()->user()->id)
->where('org_id', $request->organisation_id)
->exists();
if (!$userIsAnAdminInOrganisation) {
return response()->json([
'status' => 403,
'message' => 'You do not have permission to initiate this payment'
], 403);
}

// $gateway_id = Gateway::where('code', 'paystack')->first()->id;
$subscriptionPlan = SubscriptionPlan::find($request->plan_id);
if(!$subscriptionPlan) {
return response()->json([
'status' => 404,
'message' => 'Subscription Plan not found'
], 404);
}
$data = $validator->validated();
$data['email'] = auth()->user()->email;
$data['reference'] = Str::uuid();
$data['plan_code'] = $subscriptionPlan->paystack_plan_code;
$data['plan_id'] = $subscriptionPlan->id;
$data['amount'] = $subscriptionPlan->price;
$data['organisation_id'] = $request->organisation_id;

try {

Expand All @@ -72,12 +90,13 @@ public function initiatePaymentForPayStack(Request $request)
} catch (\Exception $e) {
return response()->json([
'status' => 500,
'message' => 'Payment Initialization Failed: ' . $e->getMessage()
'message' => 'An unexpected error occurred. Please try again later.'
// 'message' => 'Payment Initialization Failed: ' . $e->getMessage()
], 500);
}
}

public function handlePaystackCallback($id, Request $request)
public function handlePaystackCallback($organisation_id, $id, Request $request)
{
$reference = $request->query('reference');

Expand All @@ -99,6 +118,7 @@ public function handlePaystackCallback($id, Request $request)
$userSubscription = new UserSubscription;
$userSubscription->user_id = auth()->user()->id;
$userSubscription->subscription_plan_id = $id;
$userSubscription->org_id = $organisation_id;
$userSubscription->save();


Expand All @@ -115,7 +135,7 @@ public function handlePaystackCallback($id, Request $request)
public function initiatePaymentForFlutterWave(Request $request)
{
$validator = Validator::make($request->all(), [
// 'organisation_id' => 'required',
'organisation_id' => 'required',
'plan_id' =>'required',
'billing_option' => 'required|in:monthly,yearly',
'full_name' => 'required',
Expand All @@ -128,8 +148,25 @@ public function initiatePaymentForFlutterWave(Request $request)
'message' => 'Validation error: ' . $validator->errors()->first()
], 400);
}

$userIsAnAdminInOrganisation = Organisation::where('user_id', auth()->user()->id)
->where('org_id', $request->organisation_id)
->exists();
// return response()->json(auth()->user()->id);
if (!$userIsAnAdminInOrganisation) {
return response()->json([
'status' => 403,
'message' => 'You do not have permission to initiate this payment'
], 403);
}
// $gateway_id = Gateway::where('code', 'flutterwave')->first()->id;
$subscriptionPlan = SubscriptionPlan::find($request->plan_id);
if(!$subscriptionPlan) {
return response()->json([
'status' => 404,
'message' => 'Subscription Plan not found'
], 404);
}

$data = $validator->validated();
$data['email'] = auth()->user()->email;
Expand All @@ -138,6 +175,8 @@ public function initiatePaymentForFlutterWave(Request $request)
$data['plan_id'] = $subscriptionPlan->id;
$data['amount'] = $subscriptionPlan->price;
$data['title'] = $subscriptionPlan->name;
$data['organisation_id'] = $request->organisation_id;
$data['title'] = $subscriptionPlan->name;

try {
// Retrieve the gateway name
Expand Down Expand Up @@ -166,12 +205,13 @@ public function initiatePaymentForFlutterWave(Request $request)
} catch (\Exception $e) {
return response()->json([
'status' => 500,
'message' => 'Payment Initialization Failed: ' . $e->getMessage()
'message' => 'An unexpected error occurred. Please try again later.'
// 'message' => 'Payment Initialization Failed: ' . $e->getMessage()
], 500);
}
}

public function handleFlutterwaveCallback($id, Request $request)
public function handleFlutterwaveCallback($organisation_id, $id, Request $request)
{
$transaction_id = $request->query('transaction_id');

Expand All @@ -192,6 +232,7 @@ public function handleFlutterwaveCallback($id, Request $request)
$userSubscription = new UserSubscription;
$userSubscription->user_id = auth()->user()->id;
$userSubscription->subscription_plan_id = $id;
$userSubscription->org_id = $organisation_id;
$userSubscription->save();

// Redirect to the specified URL with status
Expand Down
17 changes: 17 additions & 0 deletions app/Models/Organisation.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,21 @@ public function roles()
{
return $this->hasMany(Role::class, 'org_id');
}

public function subscription()
{
return $this->hasOne(UserSubscription::class, 'org_id', 'org_id');
}

public function subscriptionPlan()
{
return $this->hasOneThrough(
SubscriptionPlan::class,
UserSubscription::class,
'org_id', // Foreign key on UserSubscription table
'id', // Foreign key on SubscriptionPlan table
'org_id', // Local key on Organisation table
'subscription_plan_id' // Local key on UserSubscription table
);
}
}
5 changes: 5 additions & 0 deletions app/Models/SubscriptionPlan.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,9 @@ public function features(): BelongsToMany
->withTimestamps();
}

public function userSubscriptions()
{
return $this->hasMany(UserSubscription::class, 'subscription_plan_id', 'id');
}

}
5 changes: 5 additions & 0 deletions app/Models/UserSubscription.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,9 @@ class UserSubscription extends Model
use HasFactory, HasUuids;

protected $fillable = ['subscription_plan_id', 'cancellation_reason'];

public function subscriptionPlan()
{
return $this->belongsTo(SubscriptionPlan::class, 'subscription_plan_id', 'id');
}
}
12 changes: 5 additions & 7 deletions app/Services/PaymentService.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ public function initiatePaystackPayment($data)
])->post('https://api.paystack.co/transaction/initialize', [
'email' => $data['email'],
'plan' => $data['plan_code'],
'amount' => $data['amount'],
'reference' => $data['reference'],
'callback_url' => url('/api/v1/payments/paystack/verify/'.$data['plan_id']),
'callback_url' => url('/api/v1/payments/paystack/'.$data['organisation_id'].'/verify/'.$data['plan_id']),
'metadata' => [
'cancel_action' => route('payment.cancel')
]
Expand Down Expand Up @@ -61,18 +62,15 @@ public function initiateFlutterwavePayment($data)
'tx_ref' => $data['reference'],
'amount' => $data['amount'], // Flutterwave still needs the amount
'currency' => 'USD',
'redirect_url' => url('/api/v1/payments/flutterwave/verify/'.$data['plan_id']),
'payment_plan' => $data['plan_code'],
'redirect_url' => url('/api/v1/payments/flutterwave/'.$data['organisation_id'].'/verify/'.$data['plan_id']),
'customer' => [
'email' => $data['email'],
'name' => $data['full_name']
],
'customizations' => [
'title' => 'Your Payment Title',
'title' => $data['title'],
'billing_option' => $data['billing_option'] // Include billing_option in customizations
],
'meta' => [
'source' => 'laravel-flutterwave',
'plan_code' => $data['plan_code'] // Include plan_code in meta
]
]);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
/**
* Run the migrations.
*/
public function up()
{
Schema::table('user_subscriptions', function (Blueprint $table) {
$table->foreignUuid('org_id')->constrained('organisations', 'org_id')->cascadeOnDelete()->cascadeOnUpdate();
});
}


/**
* Reverse the migrations.
*/
public function down(): void
{

Schema::table('user_subscriptions', function (Blueprint $table) {
$table->dropForeign(['org_id']);
$table->dropColumn('org_id');
});
}
};
4 changes: 2 additions & 2 deletions routes/api.php
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,9 @@
Route::apiResource('/features', FeatureController::class);
Route::apiResource('/plans', SubscriptionController::class);
Route::post('/payments/paystack', [PaymentController::class, 'initiatePaymentForPayStack']);
Route::get('/payments/paystack/verify/{id}', [PaymentController::class, 'handlePaystackCallback']);
Route::get('/payments/paystack/{organisation_id}/verify/{id}', [PaymentController::class, 'handlePaystackCallback']);
Route::post('/payments/flutterwave', [PaymentController::class, 'initiatePaymentForFlutterWave']);
Route::get('/payments/flutterwave/verify/{id}', [PaymentController::class, 'handleFlutterwaveCallback']);
Route::get('/payments/flutterwave/{organisation_id}/verify/{id}', [PaymentController::class, 'handleFlutterwaveCallback']);
Route::get('/payments/cancel', [PaymentController::class, 'cancel'])->name('payment.cancel');
Route::post('/users/plans/{user_subscription}/cancel', [\App\Http\Controllers\Api\V1\User\SubscriptionController::class, 'destroy']);
Route::get('/users/plan', [\App\Http\Controllers\Api\V1\User\SubscriptionController::class, 'userPlan']);
Expand Down

0 comments on commit 81e0e42

Please sign in to comment.