Files
api/services/nginx/app/objects/stripe_payment_intents_o.php
T

243 lines
7.7 KiB
PHP

<?php
namespace objects;
use classes\db;
use classes\object_property;
use classes\stripe;
use Exception;
use traits\db_object_t;
class stripe_payment_intents_o extends db
{
use db_object_t;
public object_property $order_id;
public object_property $payment_intent_id;
public object_property $client_secret;
public object_property $data;
public object_property $reader_id;
public object_property $tax_percentage;
public function structure(): void
{
$this->setTable('stripe_payment_intents');
}
/**
* Add a new payment intent
* @throws Exception
*/
public function add(
int $order_id,
string $payment_intent_id,
string $client_secret,
mixed $data = null,
?string $reader_id = null,
int $tax_percentage = null
): void {
if (is_object($data) || is_array($data)) {
$data = json_encode($data);
}
$this->clearOrderPaymentIntents($order_id);
$tmp_id = self::add_object([
'order_id' => $order_id,
'payment_intent_id' => $payment_intent_id,
'client_secret' => $client_secret,
'data' => $data,
'reader_id' => $reader_id,
'tax_percentage' => ($tax_percentage !== null) ? (int)$tax_percentage : 0,
]);
$this->id = $tmp_id;
self::getObjectProperties();
self::objectChanged();
}
public function getObjectProperties(): void
{
$this->order_id = new object_property($this->table, $this->id, 'order_id', 'int', false);
$this->payment_intent_id = new object_property($this->table, $this->id, 'payment_intent_id', 'string', false);
$this->client_secret = new object_property($this->table, $this->id, 'client_secret', 'string', false);
$this->data = new object_property($this->table, $this->id, 'data', 'string', false);
$this->reader_id = new object_property($this->table, $this->id, 'reader_id', 'string', false);
$this->tax_percentage = new object_property($this->table, $this->id, 'tax_percentage', 'int', false);
}
public function objectChanged(): void
{
// TODO: Add cache invalidation
}
public function doesOrderHavePaymentIntent(int $order_id): bool
{
return count($this->getOrderPaymentIntentRows($order_id)) > 0;
}
/**
* @throws Exception
*/
public function selectOrderPaymentIntent(int $order_id): self
{
$rows = $this->getOrderPaymentIntentRows($order_id);
if (count($rows) === 0) {
throw new Exception('No payment intent found for order id: ' . $order_id);
}
$this->id = (int)$rows[0]['id'];
self::getObjectProperties();
$this->deleteDuplicateOrderPaymentIntents($order_id, $this->id);
return $this;
}
/**
* @return array<int, array<string, mixed>>
*/
public function getOrderPaymentIntentRows(int $order_id): array
{
$rows = self::getFieldsWhere(
[
'order_id' => $order_id,
],
['id', 'payment_intent_id', 'reader_id', 'tax_percentage']
);
usort($rows, static fn(array $a, array $b): int => ((int)($b['id'] ?? 0)) <=> ((int)($a['id'] ?? 0)));
return $rows;
}
public function clearOrderPaymentIntents(int $order_id, ?int $keepId = null): void
{
foreach ($this->getOrderPaymentIntentRows($order_id) as $row) {
$rowId = (int)($row['id'] ?? 0);
if ($rowId <= 0 || ($keepId !== null && $rowId === $keepId)) {
continue;
}
self::delete_object($this->getTable(), $rowId);
}
}
public function updateStoredPaymentIntent(mixed $paymentIntent): void
{
self::requireSelected();
if (is_object($paymentIntent) && method_exists($paymentIntent, 'toJSON')) {
$encodedData = $paymentIntent->toJSON();
} elseif (is_array($paymentIntent) || is_object($paymentIntent)) {
$encodedData = json_encode($paymentIntent);
} else {
$encodedData = (string)$paymentIntent;
}
$this->data->set($encodedData);
$normalizedClientSecret = null;
$normalizedReaderId = null;
$normalizedTaxPercentage = null;
if (is_object($paymentIntent) || is_array($paymentIntent)) {
$metadata = is_array($paymentIntent)
? ($paymentIntent['metadata'] ?? [])
: ($paymentIntent->metadata ?? null);
$normalizedClientSecret = is_array($paymentIntent)
? ($paymentIntent['client_secret'] ?? null)
: ($paymentIntent->client_secret ?? null);
if (is_array($metadata)) {
$normalizedReaderId = $metadata['reader_id'] ?? $metadata['reader'] ?? null;
$normalizedTaxPercentage = $metadata['tax_percentage'] ?? null;
} elseif (is_object($metadata)) {
$normalizedReaderId = $metadata->reader_id ?? $metadata->reader ?? null;
$normalizedTaxPercentage = $metadata->tax_percentage ?? null;
}
}
if (is_string($normalizedClientSecret) && $normalizedClientSecret !== '') {
$this->client_secret->set($normalizedClientSecret);
}
if ($normalizedReaderId !== null) {
$this->reader_id->set((string)$normalizedReaderId);
}
if ($normalizedTaxPercentage !== null && is_numeric($normalizedTaxPercentage)) {
$this->tax_percentage->set((int)$normalizedTaxPercentage);
}
self::objectChanged();
}
public function setReaderId(?string $readerId): void
{
self::requireSelected();
if ($readerId === null || trim($readerId) === '') {
$this->reader_id->nullify();
self::objectChanged();
return;
}
$this->reader_id->set(trim($readerId));
self::objectChanged();
}
/**
* @throws Exception
*/
public function delete(): void
{
self::requireSelected();
self::cancelPaymentIntent();
self::deletePermanently();
}
/**
* @throws Exception
*/
public function cancelPaymentIntent(): void
{
self::requireSelected();
$stripe = new stripe();
$readerId = trim((string)($this->reader_id->value() ?? ''));
if ($readerId !== '') {
try {
$stripe->readers->sendCancelPaymentIntent($readerId);
} catch (\Stripe\Exception\InvalidRequestException) {
// The reader may already be idle or missing. Clearing local state is sufficient.
}
$this->reader_id->nullify();
}
$paymentIntentId = trim((string)($this->payment_intent_id->value() ?? ''));
if ($paymentIntentId === '') {
self::objectChanged();
return;
}
try {
$paymentIntent = $stripe->payment_intents->get($paymentIntentId);
} catch (\Stripe\Exception\InvalidRequestException) {
self::objectChanged();
return;
}
$status = strtolower((string)($paymentIntent->status ?? ''));
if (in_array($status, ['succeeded', 'canceled'], true)) {
$this->updateStoredPaymentIntent($paymentIntent);
return;
}
try {
$cancelledPaymentIntent = $stripe->payment_intents->cancel($paymentIntentId);
$this->updateStoredPaymentIntent($cancelledPaymentIntent);
} catch (\Stripe\Exception\InvalidRequestException) {
self::objectChanged();
}
}
private function deleteDuplicateOrderPaymentIntents(int $order_id, int $keepId): void
{
$this->clearOrderPaymentIntents($order_id, $keepId);
}
}