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,
);
}
}