#nikolay_code
1 messages · Page 1 of 1 (latest)
👋 Welcome to your new thread!
⏲️ We'll be here soon! Typically we respond in a few minutes, but sometimes we might take a bit longer if the server is busy or if you have a particularly tricky question.
⏱️ We close idle threads, which makes them read-only. Once a thread is closed it won't be reopened, but you can always start a new thread if you have another question.
🔗 This thread will always be available, even after it's closed. You can find it again using Discord's search, or you can save this link: https://discord.com/channels/841573134531821608/1381623727808053260
📝 Have more to share? Add more details, code, screenshots, videos, etc. below.
Full code:
Hi, what integration guide are you following?
- stripe elements:
Can you share the example request to create the payment intent please? Here's how you can find a request ID: https://support.stripe.com/questions/finding-the-id-for-an-api-request
payment_intent.created
Inspect event detail in Workbench
Event data
{
"id": "pi_3RXmsn1VlpCPYqMR1XPGDNdO",
"object": "payment_intent",
"last_payment_error": null,
"livemode": false,
"next_action": null,
"status": "requires_payment_method",
"amount": 100,
"amount_capturable": 0,
"amount_details": {
"tip": {
}
},
"amount_received": 0,
"application": null,
"application_fee_amount": null,
"automatic_payment_methods": null,
"canceled_at": null,
"cancellation_reason": null,
"capture_method": "automatic",
"client_secret": "pi_3RXmsn1VlpCPYqMR1XPGDNdO_secret_AnSIDC0U9kZncFfuKPkSJBsR0",
"confirmation_method": "automatic",
"created": 1749403185,
"currency": "gbp",
"customer": "cus_SSiJPCeSwTyZqd",
"description": "Subscription creation",
"latest_charge": null,
"metadata": {
},
"on_behalf_of": null,
"payment_method": null,
"payment_method_configuration_details": null,
"payment_method_options": {
"card": {
"installments": null,
"mandate_options": null,
"network": null,
"request_three_d_secure": "automatic"
}
},
"payment_method_types": [
"card"
],
"processing": null,
"receipt_email": null,
"review": null,
"setup_future_usage": "off_session",
"shipping": null,
"source": null,
"statement_descriptor": null,
"statement_descriptor_suffix": null,
"transfer_data": null,
"transfer_group": null
}
are you still there?
If you look at the status of the payment intent, you can see that it requires confirmation: https://dashboard.stripe.com/test/events/evt_3RXmsn1VlpCPYqMR1qHA8akc
We document this here: https://docs.stripe.com/billing/subscriptions/build-subscriptions?platform=web&ui=elements#create-subscription
On the backend, create the subscription with status incomplete using payment_behavior=default_incomplete. Then, return the client_secret from the subscription’s first payment intent to the frontend to complete payment by expanding the confirmation_secret on the latest invoice of the subscription.
Ive already done this here in the payments-api.php:
// 5) Create subscription (with payment_settings)
try {
error_log("ℹ️ Creating subscription for customer {$customer->id} with PM={$pmId}");
$subscription = \Stripe\Subscription::create( [
'customer' => $customer->id,
'items' => [ [ 'price' => $plan ] ],
'payment_behavior' => 'default_incomplete',
'collection_method' => 'charge_automatically',
'default_payment_method' => $pmId,
'payment_settings' => [
'payment_method_types' => ['card'],
'save_default_payment_method' => 'on_subscription',
],
'expand' => [ 'latest_invoice.payment_intent' ],
'metadata' => $metadata,
], [
'idempotency_key' => $idempotencyKey,
] );
error_log("✅ Subscription created: id={$subscription->id}, status={$subscription->status}");
error_log("ℹ️ Subscription.default_payment_method: " . ($subscription->default_payment_method ?? 'n/a'));
if ( isset( $subscription->default_payment_method ) && $subscription->default_payment_method === $pmId ) {
error_log("✅ PM {$pmId} correctly set as Subscription.default_payment_method");
} else {
error_log("❌ PM mismatch on Subscription. Expected {$pmId}, got " . print_r($subscription->default_payment_method, true));
}
} catch ( \Exception $e ) {
error_log("❌ Subscription creation failed: " . $e->getMessage() );
wp_send_json_error( [ 'message' => 'Subscription failed' ] );
error_log('---- payments-api.php END [subscription_fail] ----');
exit;
}
i then return client secret heree:
// 8) Respond to frontend (always return client_secret + requires_action)
error_log('ℹ️ Responding with payment_intent_client_secret and requires_action flags');
wp_send_json_success( [
'payment_intent_client_secret' => $pi->client_secret,
'requires_action' => $pi->status === 'requires_action',
] );
error_log('---- payments-api.php END [response] ----');
exit;
You still need to do this step: https://docs.stripe.com/billing/subscriptions/build-subscriptions?platform=web&ui=elements#complete-payment and confirm the subscription's first invoice's payment intent here
which part, please could you be a bit more specific as this confers to the entirety of the process, which exact part didnt i follow?
Use stripe.confirmPayment to complete the payment using details from the Payment Element and activate the subscription.
So you need to make that confirmation call
The reason behind using default_incomplete behavior is that it awaits explicit confirmation of the invoice's payment intent.
No, but im using unified elements, i would like to keep the individual card elements to fit the UI:
<h3 class="bhd-payment-title">PAYMENT DETAILS</h3>
</div>
<div class="bhd-form-fields">
<!-- Name on Card -->
<div class="bhd-form-group">
<label for="cardName" class="bhd-label">Card Name</label>
<input
type="text"
id="cardName"
name="cardName"
placeholder="John Smith"
required
class="bhd-input"
/>
<span class="error-message">Please enter the name on your card</span>
</div>
<!-- Card Number -->
<div class="bhd-form-group">
<label for="card-number" class="bhd-label">Card Number</label>
<div id="card-number" class="bhd-input"></div>
</div>
<!-- Expiry Date -->
<div class="bhd-form-group">
<label for="card-expiry" class="bhd-label">Expiry Date</label>
<div id="card-expiry" class="bhd-input"></div>
</div>
<!-- CVC -->
<div class="bhd-form-group">
<label for="card-cvc" class="bhd-label">CVC</label>
<div id="card-cvc" class="bhd-input"></div>
</div>
<!-- Combined error display -->
<div id="card-errors" class="error-message" role="alert"></div>
</div>
</div>
The Card Element is a legacy integration with significantly less functionality than Payment Element. Stripe strongly recommends using the Payment Element to accept payments of all kinds, including card payments.
If you're using card Element, you'd want to use https://docs.stripe.com/js/payment_intents/confirm_card_payment?type=card
But i have attached a payment method for the payment intent? Please could you explain which part of https://docs.stripe.com/js/payment_intents/confirm_card_payment?type=card does my code not follow?
I explained it here: The reason behind using default_incomplete behavior is that it awaits explicit confirmation of the invoice's payment intent.
To confirm this, you can omit the payment behavior and try making a new subscription creation request
afk, thank you for the time being
Sure
Hi there 👋 jumping in as my teammate needs to step away. Can you help me understand where your process stops working as expected; are you encountering error messages being thrown when you step through it?
I also see you're using our newest API version, in which case payment_intent is no longer a valid field on an Invoice object.
https://docs.stripe.com/changelog/basil/2025-03-31/add-support-for-multiple-partial-payments-on-invoices#use-for-payment-element-integrations
Instead you'll want to use the confirmation_secret on the Invoice object:
https://docs.stripe.com/api/invoices/object#invoice_object-confirmation_secret
Your code above that expands latest_invoice.payment_intent likely isn't working as you're expecting.