PHP code example of a7my / payment-hub

1. Go to this page and download the library: Download a7my/payment-hub library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

a7my / payment-hub example snippets


use Mifatoyeh\LaravelPaymentFramework\Facades\Payment;

// Explicitly pick a provider...
$payment = Payment::driver('stripe'); // -> ->driver('paypal') once available, etc.

// ...or omit driver() entirely and use whatever's configured as the
// default (PAYMENT_DRIVER env var, or payment.default in config/payment.php).
// Payment::charge([...]) works the same way.

$response = $payment->charge([
    'amount'   => 1000, // $10.00
    'currency' => 'USD',

    'customer' => [
        'name'  => 'Mohamed Azmy',
        'email' => '[email protected]',
    ],

    // To charge a specific, already-created Stripe payment method,
    // pass its PaymentMethod ID under 'token' — NOT 'payment_method'.
    // ('payment_method' selects a method *category* — card, wallet,
    // bank_transfer, etc. — not a specific Stripe object. Passing a
    // Stripe ID like 'pm_...' under 'payment_method' will throw.)
    'token' => 'pm_1N...',

    'metadata' => ['order_id' => 123],
]);

if ($response->isSuccessful()) {
    // $response->getTransactionId(), $response->getStatus(), ...
} elseif ($response->

// Reserve $10.00 without capturing yet (capture_method: manual).
$response = $payment->authorize(['amount' => 1000, 'currency' => 'USD', /* ... */]);
$transactionId = $response->getTransactionId()->toString();

// Later: capture the hold (full or partial amount).
$payment->capture(['transaction_id' => $transactionId, 'amount' => 1000]);

// Or release it without ever charging the customer.
$payment->void(['transaction_id' => $transactionId, 'reason' => 'Order cancelled']);

// Full refund.
$payment->refund(['transaction_id' => $transactionId, 'amount' => 1000]);

// Partial refund — same call shape, smaller amount. The response's
// getStatus() tells you Refunded vs. PartiallyRefunded based on how much
// of the original charge has now been refunded in total.
$payment->partialRefund(['transaction_id' => $transactionId, 'amount' => 300]);

// verify(): "can I trust this transaction to fulfil an order?" — a boolean.
$verification = $payment->verify(['transaction_id' => $transactionId]);
$verification->isVerified(); // bool

// lookup(): the full canonical status, for reconciliation/dashboards.
$status = $payment->lookup(['transaction_id' => $transactionId]);
$status->getStatus(); // PaymentStatus enum

// Step 1 — save a card the customer entered client-side (via Stripe.js/
// Elements), which gives you a `pm_...` PaymentMethod id.
$saved = $payment->saveCard([
    'token'       => 'pm_1N...',      // from Stripe.js on the client
    'customer_id' => (string) $user->id, // YOUR OWN customer reference
]);

$stripeCustomerId = $saved->getProviderReference(); // 'cus_...' — SAVE THIS
// e.g. $user->update(['stripe_customer_id' => $stripeCustomerId]);

// Step 2 — charge that saved card later (renewal, off-session purchase, ...).
$response = $payment->chargeToken([
    'token'                       => 'pm_1N...',       // the SAME pm_... from step 1
    'amount'                      => 1000,
    'currency'                    => 'USD',
    'customer'                    => ['name' => $user->name, 'email' => $user->email],
    'provider_customer_reference' => $user->stripe_customer_id, // from step 1
]);

$response = $payment->createSubscription([
    'amount'                      => 2000,
    'currency'                    => 'USD',
    'interval'                    => 'monthly',
    'plan_id'                     => 'price_1N...', // a Stripe Price ID you created beforehand
    'customer'                    => ['name' => $user->name, 'email' => $user->email],
    'provider_customer_reference' => $user->stripe_customer_id, // from saveCard(), same as above
    'token'                       => 'pm_1N...', // optional — omit to bill the customer's stored default card
    'trial_days'                  => 14, // optional
]);

$subscriptionId = $response->getSubscriptionId();

// Cancel immediately:
$payment->cancelSubscription(['subscription_id' => $subscriptionId]);

// Or let it run until the current billing period ends:
$payment->cancelSubscription([
    'subscription_id'      => $subscriptionId,
    'cancel_at_period_end' => true,
]);

$response = Payment::driver('stripe')->createPaymentLink([
    'amount'      => 10000,          // amount in smallest unit (cents) → $100.00
    'currency'    => 'USD',
    'description' => 'Sandbox test payment',
    'customer' => [
        'name'  => 'Mohamed Azmy',
        'email' => '[email protected]',
    ],
    'return_url' => url('/payment/success'),
    'cancel_url' => url('/payment/cancel'),
    'metadata' => [
        'order_id' => 123,
    ],
]);

// Redirect the browser to the hosted Stripe Checkout page
return redirect($response->getPaymentUrl());

use Illuminate\Database\Eloquent\Model;
use Illuminate\Contracts\Auth\Authenticatable;
use Mifatoyeh\LaravelPaymentFramework\Concerns\IsPayable;
use Mifatoyeh\LaravelPaymentFramework\Contracts\Payable;

class Order extends Model implements Payable
{
    use IsPayable;

    // Tell the trait which columns hold the amount/currency — it reads
    // them for you, no accessor methods to write. Both are optional
    // (default to 'amount'/'currency' if omitted) and can be typed or
    // untyped — either works, e.g. `protected $paymentAmountColumn = '...'`
    // is equally fine if you prefer matching Eloquent's own untyped-
    // property style ($table, $fillable, etc.).
    protected string $paymentAmountColumn = 'total_cents';
    protected string $paymentCurrencyColumn = 'currency_code';

    // Which drivers THIS model may be paid through — checked even if a
    // driver is otherwise configured and enabled application-wide.
    public function getSupportedPaymentDrivers(): array
    {
        return ['stripe', 'paymob'];
    }

    // Called unconditionally by the endpoint's own controller, regardless
    // of route middleware — this is your real security boundary, not
    // whatever middleware you do or don't attach to the route.
    public function authorizePayment(?Authenticatable $payer): bool
    {
        return $payer?->id === $this->user_id;
    }
}

use Mifatoyeh\LaravelPaymentFramework\Contracts\Payable;
use Mifatoyeh\LaravelPaymentFramework\Concerns\IsPayable;
use Mifatoyeh\LaravelPaymentFramework\Enums\Currency;
use Mifatoyeh\LaravelPaymentFramework\ValueObjects\Money;

class Package extends Model implements Payable
{
    use IsPayable;

    public function getPaymentAmount(): Money
    {
        // price is stored in major units (e.g. 40.00 SAR) — convert to halalas
        $minor = (int) round((float) $this->price * 100);

        return Money::ofMinor(max($minor, 1), Currency::SAR);
    }

    public function getPaymentCurrency(): Currency
    {
        return Currency::SAR;
    }

    public function getSupportedPaymentDrivers(): array
    {
        return ['stripe', 'paymob', 'myfatoorah'];
    }

    public function authorizePayment(?Authenticatable $payer): bool
    {
        return $payer !== null;
    }
}

use Mifatoyeh\LaravelPaymentFramework\Contracts\CapturesCheckoutContext;
use Mifatoyeh\LaravelPaymentFramework\Checkout\CheckoutContext;
use Mifatoyeh\LaravelPaymentFramework\Enums\PaymentStatus;
use Mifatoyeh\LaravelPaymentFramework\Responses\StatusResponse;

class Package extends Model implements Payable, CapturesCheckoutContext
{
    // ... getPaymentAmount(), etc.

    public function captureCheckoutContext(): array
    {
        return [
            'price' => $this->price,
            // JSON-serialisable scalars/arrays only — not Eloquent models
        ];
    }

    public function onPaymentCompleted(StatusResponse $status, CheckoutContext $context): void
    {
        if (! $status->isSuccessful() || $status->getStatus() !== PaymentStatus::Captured) {
            return;
        }

        $transactionId = $status->getTransactionId()->toString();

        // Idempotent — webhooks and confirm() can both fire
        if (Transaction::where('transaction_id', $transactionId)->exists()) {
            return;
        }

        $payer = $context->payer();
        if ($payer === null) {
            return;
        }

        Transaction::create([
            'transaction_id' => $transactionId,
            'user_id'        => $payer->getAuthIdentifier(),
            'package_id'     => $this->id,
            'amount'         => $context->get('price', $this->price),
            'payment_method' => $context->driver,
        ]);
    }
}

// config/payment.php
'payables' => [
    'order'   => \App\Models\Order::class,
    'package' => \App\Models\Package::class,
],

'checkout' => [
    'enabled'    => env('PAYMENT_CHECKOUT_ENABLED', true),
    'route'      => env('PAYMENT_CHECKOUT_ROUTE', 'payment/checkout'),
    // Default is ['web', 'auth'] — use API auth for SPA/mobile backends:
    'middleware' => ['api', 'auth:api'],
],
bash
php artisan vendor:publish --tag=payment-config
php artisan vendor:publish --tag=payment-migrations
php artisan migrate

POST /payment/checkout