Payment Hold (Authorization) Implementation

Our company is engaged in the development, support and maintenance of sites of any complexity. From simple one-page sites to large-scale cluster systems built on micro services. Experience of developers is confirmed by certificates from vendors.
Development and maintenance of all types of websites:
Informational websites or web applications
Business card websites, landing pages, corporate websites, online catalogs, quizzes, promo websites, blogs, news resources, informational portals, forums, aggregators
E-commerce websites or web applications
Online stores, B2B portals, marketplaces, online exchanges, cashback websites, exchanges, dropshipping platforms, product parsers
Business process management web applications
CRM systems, ERP systems, corporate portals, production management systems, information parsers
Electronic service websites or web applications
Classified ads platforms, online schools, online cinemas, website builders, portals for electronic services, video hosting platforms, thematic portals

These are just some of the technical types of websites we work with, and each of them can have its own specific features and functionality, as well as be customized to meet the specific needs and goals of the client.

Our competencies:
Development stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1215
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    852
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1041
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    823
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Website development for FIXPER company
    815

Implementation of Payment Hold on Website

Payment hold (authorization without debit) is mechanism where money blocked on customer card but not received by seller until explicit confirmation. Used where final amount or order completion unknown at payment time: delivery with payment on fact, booking with variable cost, pre-order.

How Two-Stage Payment Works

Process consists of two operations: authorize (block funds) and capture (actual debit). Arbitrary time gap between them, but limited: most banks remove hold after 7 days, some after 30. If capture doesn't happen — hold automatically removed, money returns.

Stripe: authorize + capture

In Stripe two-stage payment via capture_method: manual:

$paymentIntent = \Stripe\PaymentIntent::create([
    'amount'         => $order->total_cents,
    'currency'       => 'eur',
    'capture_method' => 'manual', // don't debit immediately
    'payment_method_types' => ['card'],
    'metadata'       => ['order_id' => $order->id],
]);

After 3DS confirmation by customer PaymentIntent goes to requires_capture status. Money blocked, not debited. When order ready to ship:

\Stripe\PaymentIntent::capture($order->stripe_payment_intent_id, [
    'amount_to_capture' => $finalAmountCents, // can be less than authorized
]);

Capture can be done for amount less than blocked — e.g. if some goods unavailable. Cannot be more.

YooKassa: two-stage payment

$payment = $client->createPayment([
    'amount'   => ['value' => '1500.00', 'currency' => 'RUB'],
    'capture'  => false, // two-stage payment
    'payment_method_data' => ['type' => 'bank_card'],
    'confirmation' => ['type' => 'redirect', 'return_url' => $returnUrl],
    'description' => 'Order #' . $order->id,
], uniqid('', true));

When ready to debit:

$client->capturePayment(
    ['amount' => ['value' => '1500.00', 'currency' => 'RUB']],
    $order->yookassa_payment_id,
    uniqid('', true)
);

Cancelling Hold (cancel authorization)

If order cancelled before capture — must explicitly cancel authorization, else money stays blocked until expiry:

// Stripe
\Stripe\PaymentIntent::cancel($order->stripe_payment_intent_id, [
    'cancellation_reason' => 'abandoned',
]);

// YooKassa
$client->cancelPayment($order->yookassa_payment_id, uniqid('', true));

Monitoring Expiring Holds

Authorization expiry is critical parameter. Need background process finding holds close to expiry:

// Command run hourly
$expiringSoon = Order::where('payment_status', 'authorized')
    ->where('authorized_at', '<', now()->subDays(6)) // Stripe gives 7 days
    ->get();

foreach ($expiringSoon as $order) {
    // Notify manager or auto-cancel
    Notification::send($order->manager, new AuthorizationExpiring($order));
}

Store authorized_at in orders table — mandatory. Stripe limit 7 days for most cards, up to 31 for some. Better aim for 5–6 days as safe limit.

Partial Capture

Stripe supports partial capture — capture less than authorized. Difference automatically unblocked. Useful for variable shipping cost scenarios: authorize max, charge real cost by fact.

\Stripe\PaymentIntent::capture($intentId, [
    'amount_to_capture' => $actualAmountCents,
]);
// Remainder ($authorizedAmount - $actualAmount) auto-unblocked

CloudPayments and most Russian providers also support partial capture, but syntax and behavior check in specific gateway docs — differences exist.