Related
Here is how I'm initializing the Checkout Process in Node.js.
let email = req.body.email
let product_id = req.body.product_id
let YOUR_DOMAIN = 'http://localhost:8000'
const session = await stripe.checkout.sessions.create({
line_items: [{
price: product_id,
adjustable_quantity: {
enabled: true,
minimum: 1,
maximum: 5,
},
quantity: 1,
}],
customer_email: email,
mode: 'payment',
success_url: `${YOUR_DOMAIN}/payment-success`,
cancel_url: `${YOUR_DOMAIN}/payment-failure`,
});
Here are the events I have subscribed to in Webhooks
payment_intent.succeeded
checkout.session.completed
Object Received in Payment Intent Succeeded
{
"id": "evt_1KEqIxI5cib7rtVMoKMkak96",
"object": "event",
"api_version": "2020-03-02",
"created": 1641453754,
"data": {
"object": {
"id": "pi_1KEqIeI5cib7rtVMcz8zsXys",
"object": "payment_intent",
"amount": 59700,
"amount_capturable": 0,
"amount_received": 59700,
"application": null,
"application_fee_amount": null,
"automatic_payment_methods": null,
"canceled_at": null,
"cancellation_reason": null,
"capture_method": "automatic",
"charges": {
"object": "list",
"data": [{
"id": "ch_1KEqIvI5cib7rtVMYUVn5gfQ",
"object": "charge",
"amount": 59700,
"amount_captured": 59700,
"amount_refunded": 0,
"application": null,
"application_fee": null,
"application_fee_amount": null,
"balance_transaction": "txn_1KEqIwI5cib7rtVMGfJlEahg",
"billing_details": {
"address": {
"city": "NY",
"country": "US",
"line1": "sada",
"line2": "asd",
"postal_code": "10001",
"state": "NY"
},
"email": "test#gmail.com",
"name": "John Doe",
"phone": null
},
"calculated_statement_descriptor": "VIDEOFORM",
"captured": true,
"created": 1641453753,
"currency": "usd",
"customer": "cus_KufeYc1hifvZGA",
"description": null,
"destination": null,
"dispute": null,
"disputed": false,
"failure_code": null,
"failure_message": null,
"fraud_details": {},
"invoice": null,
"livemode": false,
"metadata": {},
"on_behalf_of": null,
"order": null,
"outcome": {
"network_status": "approved_by_network",
"reason": null,
"risk_level": "normal",
"risk_score": 25,
"seller_message": "Payment complete.",
"type": "authorized"
},
"paid": true,
"payment_intent": "pi_1KEqIeI5cib7rtVMcz8zsXys",
"payment_method": "pm_1KEqIuI5cib7rtVMYPWlLV8S",
"payment_method_details": {
"card": {
"brand": "visa",
"checks": {
"address_line1_check": "pass",
"address_postal_code_check": "pass",
"cvc_check": "pass"
},
"country": "US",
"exp_month": 1,
"exp_year": 2023,
"fingerprint": "QaiTi5PATFO8ZoC3",
"funding": "credit",
"installments": null,
"last4": "4242",
"mandate": null,
"network": "visa",
"three_d_secure": null,
"wallet": null
},
"type": "card"
},
"receipt_email": null,
"receipt_number": null,
"receipt_url": "https://pay.stripe.com/receipts/acct_1GhUSuI5cib7rtVM/ch_1KEqIvI5cib7rtVMYUVn5gfQ/rcpt_KufeysPW3LD8Yu47q14iYOIIlcGth9h",
"refunded": false,
"refunds": {
"object": "list",
"data": [],
"has_more": false,
"total_count": 0,
"url": "/v1/charges/ch_1KEqIvI5cib7rtVMYUVn5gfQ/refunds"
},
"review": null,
"shipping": null,
"source": null,
"source_transfer": null,
"statement_descriptor": null,
"statement_descriptor_suffix": null,
"status": "succeeded",
"transfer_data": null,
"transfer_group": null
}],
"has_more": false,
"total_count": 1,
"url": "/v1/charges?payment_intent=pi_1KEqIeI5cib7rtVMcz8zsXys"
},
"client_secret": "pi_1KEqIeI5cib7rtVMcz8zsXys_secret_uZwm3uziwfaWePgTHENYXeraD",
"confirmation_method": "automatic",
"created": 1641453736,
"currency": "usd",
"customer": "cus_KufeYc1hifvZGA",
"description": null,
"invoice": null,
"last_payment_error": null,
"livemode": false,
"metadata": {},
"next_action": null,
"on_behalf_of": null,
"payment_method": "pm_1KEqIuI5cib7rtVMYPWlLV8S",
"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": null,
"shipping": null,
"source": null,
"statement_descriptor": null,
"statement_descriptor_suffix": null,
"status": "succeeded",
"transfer_data": null,
"transfer_group": null
}
},
"livemode": false,
"pending_webhooks": 3,
"request": {
"id": "req_Y87umQuitz9WCc",
"idempotency_key": "a4b6af76-73d8-49b4-8393-9017e673af7e"
},
"type": "payment_intent.succeeded"
}
Object Received in Checkout Session Completed
{
"id": "evt_1KEqQOI5cib7rtVMRwAHWDvV",
"object": "event",
"api_version": "2020-03-02",
"created": 1641454214,
"data": {
"object": {
"id": "cs_test_a1AQ9EKIaxi8dXFAJzZ86xYm5meRkWb9Vu0eLowfkiKgUO4cyToooBzSTd",
"object": "checkout.session",
"after_expiration": null,
"allow_promotion_codes": null,
"amount_subtotal": 59700,
"amount_total": 59700,
"automatic_tax": {
"enabled": false,
"status": null
},
"billing_address_collection": null,
"cancel_url": "http://localhost:8000/payment-failure",
"client_reference_id": null,
"consent": null,
"consent_collection": null,
"currency": "usd",
"customer": "cus_Kuflo6N0n4HTzL",
"customer_details": {
"email": "test#gmail.com",
"phone": null,
"tax_exempt": "none",
"tax_ids": []
},
"customer_email": "test#gmail.com",
"expires_at": 1641540596,
"livemode": false,
"locale": null,
"mode": "payment",
"payment_intent": "pi_1KEqQ4I5cib7rtVMyND6NhQG",
"payment_method_options": {},
"payment_method_types": ["card"],
"payment_status": "paid",
"phone_number_collection": {
"enabled": false
},
"recovered_from": null,
"setup_intent": null,
"shipping": null,
"shipping_address_collection": null,
"shipping_options": [],
"shipping_rate": null,
"status": "complete",
"submit_type": null,
"subscription": null,
"success_url": "http://localhost:8000/payment-success",
"total_details": {
"amount_discount": 0,
"amount_shipping": 0,
"amount_tax": 0
},
"url": null
}
},
"livemode": false,
"pending_webhooks": 4,
"request": {
"id": null,
"idempotency_key": null
},
"type": "checkout.session.completed"
}
I'm unable to figure out how to get the Product ID and the Quantity. Could anyone suggest the right approach here?
You can find the Product and the quantity from the Checkout Session by using expand on the line_items property. The code should look like this.
// In "checkout.session.completed" event handler, get the ID of the Checkout Session
const id = event.data.object.id; // "cs_xxx"
// Retrieve the Checkout Session with expand
const session = await stripe.checkout.sessions.retrieve(id, {
expand: [ "line_items" ]
});
// Get the quantity
console.log(session.line_items.data[0].quantity);
// Get the product ID
console.log(session.line_items.data[0].price.product);
I am trying to get line_items in the 2020/08 stripe API version over webhooks, but it is not returned in the webhook. My code is:
$session = \Stripe\Checkout\Session::create([
'customer' => $user,
'payment_method_types' => ['card'],
//'metadata' => ['item_id' => '6735'],
'line_items' => [[
'name' => 'cows',//$name,
'description' => $description,
'images' => ['https://www.mathtutortime.com/account/add_time/selected/tutoring.jpg'],
'amount' => 0050.00,
'currency' => 'usd',
'quantity' => 1,
],
],
'mode' => 'payment',
'success_url' =>'https://www.mathtutortime.com/account/add_time/selected/success2?session_id={CHECKOUT_SESSION_ID}&bought='.urlencode($description).'',
'cancel_url' => 'https://www.mathtutortime.com/account/add_time/selected/failure',
]);
, and what is returned in the checkout session event is:
{
"id": "evt_1IuOvpBaP49eHvWwXFoFZzsB",
"object": "event",
"api_version": "2020-08-27",
"created": 1621805637,
"data": {
"object": {
"id": "cs_test_a1U80BcqqjXeudvxrYDCUr34w5YZBUCKRN8qCdCMx7wGWnGMhtrP7I3xFk",
"object": "checkout.session",
"allow_promotion_codes": null,
"amount_subtotal": 50,
"amount_total": 50,
"billing_address_collection": null,
"cancel_url": "https://www.mathtutortime.com/account/add_time/selected/failure",
"client_reference_id": null,
"currency": "usd",
"customer": "rickster26ter4",
"customer_details": {
"email": "rick.giovanini1#gmail.com",
"tax_exempt": "none",
"tax_ids": [
]
},
"customer_email": null,
"livemode": false,
"locale": null,
"metadata": {
},
"mode": "payment",
"payment_intent": "pi_1IuOvFBaP49eHvWw9jkdH1nb",
"payment_method_options": {
},
"payment_method_types": [
"card"
],
"payment_status": "paid",
"setup_intent": null,
"shipping": null,
"shipping_address_collection": null,
"submit_type": null,
"subscription": null,
"success_url": "https://www.mathtutortime.com/account/add_time/selected/success2?session_id={CHECKOUT_SESSION_ID}&bought=1+Hour+of+Tutoring+%2B+Premium+Dashboard+Features",
"total_details": {
"amount_discount": 0,
"amount_shipping": 0,
"amount_tax": 0
}
}
},
"livemode": false,
"pending_webhooks": 1,
"request": {
"id": null,
"idempotency_key": null
},
"type": "checkout.session.completed"
}
There is no line_items, or even "name". Also, I get the error returned from my webhook:
<br />
<b>Notice</b>: Trying to get property 'custom' of non-object in <b>/var/www/html/account/add_time/selected/success/webhook.php</b> on line <b>30</b><br />
<br />
<b>Notice</b>: Trying to get property 'name' of non-object in <b>/var/www/html/account/add_time/selected/success/webhook.php</b> on line <b>30</b><br />
Thanks for any help. Stripe has not been much of any help so far...
line_items are not returned by default. You need to re-retrieve the Checkout Session and expand line_items.
My website's users will buy credits using Stripe's single-payment workflow with a checkout session. They can buy single credits or credit packages, like 10 credits package at discount price.
After a successfully payment I want to capture the event with a webhook and update the user balance according to the product and quantity paid by the user.
I'm currently tracking payment_intent.succeeded events and I can see payment and charge information however I can't find any information related to the product ordered. What am I missing? Thank you.
This is an example of the payment intent event I'm capturing:
{
"amount": 5250,
"amount_capturable": 0,
"amount_received": 5250,
"application": null,
"application_fee_amount": null,
"canceled_at": null,
"cancellation_reason": null,
"capture_method": "automatic",
"charges": {
"data": [
{
"amount": 5250,
"amount_captured": 5250,
"amount_refunded": 0,
"application": null,
"application_fee": null,
"application_fee_amount": null,
"balance_transaction": "txn_1HUSonCfZ37XLQD8mLERR2YR",
"billing_details": {
"address": {
"city": null,
"country": "JP",
"line1": null,
"line2": null,
"postal_code": null,
"state": null
},
"email": "xxxx.xxxx.xxxx#gmail.com",
"name": "asd",
"phone": null
},
"calculated_statement_descriptor": "XXX XXXX",
"captured": true,
"created": 1600847712,
"currency": "jpy",
"customer": "cus_I4c27DIUQuvAHQ",
"description": null,
"destination": null,
"dispute": null,
"disputed": false,
"failure_code": null,
"failure_message": null,
"fraud_details": {},
"id": "ch_1HUSomCfZ37XLQD8d133buOK",
"invoice": null,
"livemode": false,
"metadata": {},
"object": "charge",
"on_behalf_of": null,
"order": null,
"outcome": {
"network_status": "approved_by_network",
"reason": null,
"risk_level": "normal",
"risk_score": 39,
"seller_message": "Payment complete.",
"type": "authorized"
},
"paid": true,
"payment_intent": "pi_1HUSoWCfZ37XLQD82vnE1yQT",
"payment_method": "pm_1HUSolCfZ37XLQD8gSn0oy4x",
"payment_method_details": {
"card": {
"brand": "visa",
"checks": {
"address_line1_check": null,
"address_postal_code_check": null,
"cvc_check": "pass"
},
"country": "US",
"exp_month": 11,
"exp_year": 2050,
"fingerprint": "uaJa23vzDgA7fnSC",
"funding": "credit",
"installments": null,
"last4": "4242",
"network": "visa",
"three_d_secure": null,
"wallet": null
},
"type": "card"
},
"receipt_email": null,
"receipt_number": null,
"receipt_url": "https://pay.stripe.com/receipts/acct_1HU77uCfZ37XLQD8/ch_1HUSomCfZ37XLQD8d133buOK/rcpt_I4c233eGeSYOaN9cPvncC4AcU2Sm4s7",
"refunded": false,
"refunds": {},
"review": null,
"shipping": null,
"source": null,
"source_transfer": null,
"statement_descriptor": null,
"statement_descriptor_suffix": null,
"status": "succeeded",
"transfer_data": null,
"transfer_group": null
}
],
"has_more": false,
"object": "list",
"total_count": 1,
"url": "/v1/charges?payment_intent=pi_1HUSoWCfZ37XLQD82vnE1yQT"
},
"client_secret": "pi_1HUSoWCfZ37XLQD82vnE1yQT_secret_XXX",
"confirmation_method": "automatic",
"created": 1600847696,
"currency": "jpy",
"customer": "cus_I4c27DIUQuvAHQ",
"description": null,
"id": "pi_1HUSoWCfZ37XLQD82vnE1yQT",
"invoice": null,
"last_payment_error": null,
"livemode": false,
"metadata": {},
"next_action": null,
"object": "payment_intent",
"on_behalf_of": null,
"payment_method": "pm_1HUSolCfZ37XLQD8gSn0oy4x",
"payment_method_options": {
"card": {
"installments": null,
"network": null,
"request_three_d_secure": "automatic"
}
},
"payment_method_types": [
"card"
],
"receipt_email": null,
"review": null,
"setup_future_usage": null,
"shipping": null,
"source": null,
"statement_descriptor": null,
"statement_descriptor_suffix": null,
"status": "succeeded",
"transfer_data": null,
"transfer_group": null
}
Assuming you've put the product details in the line_items of the Checkout session you created (see guide step), then what you're looking for is how to "fulfill the order" (see guide step), which you can do by listening to checkout.session.completed events.
When you receive an event, the event data will be a Checkout session by id, and you can then retrieve that session and specify expand\[\]=line_items to be able to inspect the line_items for the session.
Update: I modified the above to reflect that line_items is not included by default. You must retrieve the session and include it in an expand.
I'm using Intent API(currency, amount only) and confirmCardPayment and Stripe rendered HTML input to checkout.
Now I want to implement saved credit cards, I can save it, but I don't know how to use it to pass to confirmCardPayment.
My currently working confirmCardPayment code is this
Stripe.confirmCardPayment(intent.client_secret, {
payment_method: {
/**
* I'm using Vue here
* If I'm using a saved card, what should I pass here?
**/
card: this.$refs.stripeCardRef.cardNumberElement,
},
setup_future_usage: "off_session",
});
If I'm using a saved card, what should I pass to confirmCardPayment, and also Intent API(I'm using currency, amount only right now)?
So I can use stripe.paymentMethods.list to get a customer's all saved credit cards, and return it to the front-end like this:
// Server
export async function listPaymentMethods(userId: string) {
const customer = await getOrCreateCustomer(userId);
return stripe.paymentMethods.list({
customer: customer.id,
type: "card",
});
}
Then, my front-end get the response like this
{
"object": "list",
"data": [
{
"id": "pm_1GuBTiICxYQTNr22LwKB6rfy",
"object": "payment_method",
"billing_details": {
"address": {
"city": null,
"country": null,
"line1": null,
"line2": null,
"postal_code": null,
"state": null
},
"email": null,
"name": null,
"phone": null
},
"card": {
"brand": "visa",
"checks": {
"address_line1_check": null,
"address_postal_code_check": null,
"cvc_check": "pass"
},
"country": "US",
"exp_month": 1,
"exp_year": 2023,
"fingerprint": "riI755UKjfafxa3C",
"funding": "credit",
"generated_from": null,
"last4": "4242",
"networks": {
"available": ["visa"],
"preferred": null
},
"three_d_secure_usage": {
"supported": true
},
"wallet": null
},
"created": 1592201250,
"customer": "cus_HPOXLjeqF24rBn",
"livemode": false,
"metadata": [],
"type": "card"
}
],
"has_more": false,
"url": "/v1/payment_methods"
}
How can I use this response data to confirmCardPayment?
Should my intent API need to be changed also?
This flow is covered in Stripe's documentation here: https://stripe.com/docs/payments/save-and-reuse#web-create-payment-intent-off-session
The idea is that you pass the existing PaymentMethod id pm_12345 client-side when you confirm the PaymentIntent like this:
stripe.confirmCardPayment(
intent.client_secret,
{
payment_method: intent.last_payment_error.payment_method.id
}
).then(function(result) {
if (result.error) {
// Show error to your customer
console.log(result.error.message);
} else {
if (result.paymentIntent.status === 'succeeded') {
// The payment is complete!
}
}
});
I am trying to get customer_id of a stripe customer from the response of of charge entity. But response is not providing the customer id in return.
&stripe.Charge JSON: {
"id": "ch_1AxWbTFytruJp2FXW6iuRd1X",
"object": "charge",
"amount": 100,
"amount_refunded": 0,
"application": null,
"application_fee": null,
"balance_transaction": "txn_17JOXKFytruJp2FXS4XNisQd",
"captured": false,
"created": 1504339423,
"currency": "usd",
"customer": null,
"description": "My First Test Charge (created for API docs)",
"destination": null,
"dispute": null,
"failure_code": null,
"failure_message": null,
"fraud_details": {
},
"invoice": null,
"livemode": false,
"metadata": {
},
"on_behalf_of": null,
"order": null,
"outcome": null,
"paid": true,
"receipt_email": null,
"receipt_number": null,
"refunded": false,
"refunds": {
"object": "list",
"data": [
],
"has_more": false,
"total_count": 0,
"url": "/v1/charges/ch_1AxWbTFytruJp2FXW6iuRd1X/refunds"
},
"review": null,
"shipping": null,
"source": {
"id": "card_1AxWPmFytruJp2FXw4m0V0fN",
"object": "card",
"address_city": null,
"address_country": null,
"address_line1": null,
"address_line1_check": null,
"address_line2": null,
"address_state": null,
"address_zip": "10001",
"address_zip_check": "unchecked",
"brand": "Visa",
"country": "US",
"customer": null,
"cvc_check": "unchecked",
"dynamic_last4": null,
"exp_month": 4,
"exp_year": 2024,
"fingerprint": "sNtyd9sZ2vA6o4IM",
"funding": "credit",
"last4": "4242",
"metadata": {
},
"name": "Mandeep",
"tokenization_method": null
},
"source_transfer": null,
"statement_descriptor": null,
"status": "succeeded",
"transfer_group": null
}
But it has a customer field inside the object which is null. Can anyone please tell me why am I getting this null?
What I am trying to do is to make a system where customer can book anonymously on site and while creating the booking the customer gets registered and charged for the total amount of the booking. I need to keep track of the customer's stripe account id and card id. So the problem is if I am creating a customer then I am not able to get its card id but when I am charging the customer then I am not able to get the customer id.
Customer Response:
&stripe.Customer JSON: {
"id": "cus_BKAxGZre2HCNIU",
"object": "customer",
"account_balance": 0,
"created": 1504339424,
"currency": "usd",
"default_source": null,
"delinquent": false,
"description": null,
"discount": null,
"email": null,
"livemode": false,
"metadata": {
},
"shipping": null,
"sources": {
"object": "list",
"data": [
],
"has_more": false,
"total_count": 0,
"url": "/v1/customers/cus_BKAxGZre2HCNIU/sources"
},
"subscriptions": {
"object": "list",
"data": [
],
"has_more": false,
"total_count": 0,
"url": "/v1/customers/cus_BKAxGZre2HCNIU/subscriptions"
}
}
When you create a charge using only the credit card token, no Stripe customer is created, you only create a payment with no associated customer.
So it's normal that you the API returns customer: null.
Instead of charging a credit card, I think you should charge a new customer.
In your backend code, you could handle the payment in 2 steps:
STEP 1: create a new customer, passing the credit card token to store
the customer's card
STEP 2: charge the customer, using the customer ID returned by
STEP 1 API call.
Doing this, you charge the customer with the credit card that is stored in customer's data.
For more details, check here: https://stripe.com/docs/quickstart#saving-card-information
Does it make sense?
You can access card_id while creating new customer using following code (in Golang):
for _, data := range customer.Sources.Values{
card_id = data.ID
}
fmt.Println(card_id)
I spend almost 1 day to figure it out. Actually in customer structure(under stripe package) there are some fields which are having embedded types and such fields are further connected to some other structure in different files. So there is hierarchy to access structure fields like above.
Hope this will solve your problem.
Thanks!