HEX
Server: LiteSpeed
System: Linux s1049.use1.mysecurecloudhost.com 4.18.0-477.27.2.lve.el8.x86_64 #1 SMP Wed Oct 11 12:32:56 UTC 2023 x86_64
User: xedaptot (3356)
PHP: 8.3.31
Disabled: NONE
Upload Files
File: /home/xedaptot/be.naniguide.com/app/Model/PaymentIntent.php
<?php

namespace App\Model;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use App\Library\Traits\HasUid;
use App\Cashier\DTO\PaymentIntent as PaymentIntentDto;
use App\Cashier\DTO\Payer;
use App\Cashier\DTO\SubscriptionSpec;

/**
 * @property int $id
 * @property string $uid
 * @property int $invoice_id
 * @property int $payment_gateway_id
 * @property string $amount
 * @property string $currency
 * @property string $description
 * @property string $status
 * @property string|null $remote_reference_id
 * @property array|null $metadata
 * @property string|null $failed_reason
 * @property \Illuminate\Support\Carbon|null $succeeded_at
 * @property int|null $payment_method_id
 * @property string|null $payment_method_snapshot
 * @property-read Invoice $invoice
 * @property-read PaymentGateway $paymentGateway
 * @property-read PaymentMethod|null $paymentMethod
 */
class PaymentIntent extends Model
{
    use HasFactory;
    use HasUid;

    protected static function newFactory()
    {
        return \Database\Factories\PaymentIntentFactory::new();
    }

    public const STATUS_PENDING                 = 'pending';
    public const STATUS_REQUIRES_ACTION         = 'requires_action';
    public const STATUS_AWAITING_ADMIN_APPROVAL = 'awaiting_admin_approval';
    public const STATUS_SUCCEEDED               = 'succeeded';
    public const STATUS_FAILED                  = 'failed';
    public const STATUS_CANCELLED               = 'cancelled';

    protected $fillable = [
        'uid',
        'invoice_id',
        'payment_gateway_id',
        'payment_method_id',
        'payment_method_snapshot',
        'amount',
        'currency',
        'description',
        'status',
        'remote_reference_id',
        'metadata',
        'failed_reason',
        'succeeded_at',
    ];

    protected $casts = [
        'amount'       => 'decimal:2',
        'metadata'     => 'array',
        'succeeded_at' => 'datetime',
    ];

    public function invoice(): BelongsTo
    {
        return $this->belongsTo(Invoice::class);
    }

    public function paymentGateway(): BelongsTo
    {
        return $this->belongsTo(PaymentGateway::class);
    }

    /**
     * Saved payment method that was used to charge this intent.
     * Nullable: NULL when the intent was Offline, when the source PaymentMethod
     * row was deleted (FK nullOnDelete), or when no saved PM was used at all.
     * Use `payment_method_snapshot` (immutable text) to distinguish "PM deleted"
     * from "never had a PM" — see docs/payment-order-plan-subscription-saas/PAYMENT-COMPREHENSIVE-DESIGN.md §14.
     */
    public function paymentMethod(): BelongsTo
    {
        return $this->belongsTo(PaymentMethod::class);
    }

    public function scopeActive($q)
    {
        return $q->whereIn('status', [
            self::STATUS_PENDING,
            self::STATUS_REQUIRES_ACTION,
            self::STATUS_AWAITING_ADMIN_APPROVAL,
        ]);
    }

    public function scopeTerminal($q)
    {
        return $q->whereIn('status', [self::STATUS_SUCCEEDED, self::STATUS_FAILED, self::STATUS_CANCELLED]);
    }

    public function scopeAwaitingAdminApproval($q)
    {
        return $q->where('status', self::STATUS_AWAITING_ADMIN_APPROVAL);
    }

    public function isPending(): bool                { return $this->status === self::STATUS_PENDING; }
    public function isRequiresAction(): bool         { return $this->status === self::STATUS_REQUIRES_ACTION; }
    public function isAwaitingAdminApproval(): bool  { return $this->status === self::STATUS_AWAITING_ADMIN_APPROVAL; }
    public function isSucceeded(): bool              { return $this->status === self::STATUS_SUCCEEDED; }
    public function isFailed(): bool                 { return $this->status === self::STATUS_FAILED; }
    public function isCancelled(): bool              { return $this->status === self::STATUS_CANCELLED; }
    public function isTerminal(): bool               { return in_array($this->status, [self::STATUS_SUCCEEDED, self::STATUS_FAILED, self::STATUS_CANCELLED]); }

    /**
     * Convert this Eloquent row into the cashier-consumable DTO.
     * Cashier never sees the Eloquent model.
     */
    public function toDto(): PaymentIntentDto
    {
        $invoice = $this->invoice;
        $customer = $invoice->customer;

        $payer = new Payer(
            uid:                $customer->uid,
            name:               $customer->getName(),
            email:              $invoice->billing_email ?: ($customer->email ?? ''),
            phone:              $invoice->billing_phone ?: '',
            billingName:        $invoice->getBillingName(),
            billingAddress:     $invoice->billing_address ?: '',
            billingCountryCode: $invoice->getBillingCountryCode() ?: '',
        );

        $subSpec = null;
        $planId = $this->metadata['remote_plan_id'] ?? null;
        if ($planId) {
            $subSpec = new SubscriptionSpec(remotePlanId: $planId);
        }

        return new PaymentIntentDto(
            uid:               $this->uid,
            amount:            (float) $this->amount,
            currency:          $this->currency,
            description:       $this->description,
            paymentGatewayId:  $this->paymentGateway->uid,
            payer:             $payer,
            subscription:      $subSpec,
            metadata:          $this->metadata ?? [],
            remoteReferenceId: $this->remote_reference_id,
        );
    }
}