PhoneFactor Country List - azure-ad-b2c
I'm using the PhoneFactor-InputOrVerify for my custom policy MFA. If a user does not have a phone number registered, it will ask them to enter one. Part of this is a dropdown list of countries that the phone is registered in (so that it can get the country code). I've managed to trim this list down to just a few countries, using the following TechnicalProfile
<TechnicalProfile Id="PhoneFactor-InputOrVerify">
<DisplayName>PhoneFactor</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.PhoneFactorProtocolProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="ContentDefinitionReferenceId">api.phonefactor</Item>
<Item Key="ManualPhoneNumberEntryAllowed">true</Item>
<Item Key="setting.authenticationMode">sms</Item>
<Item Key="setting.autodial">true</Item>
<Item Key="language.countryList"><![CDATA[{"IE": "Ireland","IM": "Isle of Man","GB": "United Kingdom","US": "United States","DE": "Germany"}]]></Item>
</Metadata>
<CryptographicKeys>
<Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" />
</CryptographicKeys>
<InputClaimsTransformations>
<InputClaimsTransformation ReferenceId="CreateUserIdForMFA" />
</InputClaimsTransformations>
<InputClaims>
<InputClaim ClaimTypeReferenceId="userIdForMFA" PartnerClaimType="UserId" />
<InputClaim ClaimTypeReferenceId="strongAuthenticationPhoneNumber" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="Verified.strongAuthenticationPhoneNumber" PartnerClaimType="Verified.OfficePhone" />
<OutputClaim ClaimTypeReferenceId="newPhoneNumberEntered" PartnerClaimType="newPhoneNumberEntered" />
<OutputClaim ClaimTypeReferenceId="isActiveMFASession" DefaultValue="true"/>
</OutputClaims>
</TechnicalProfile>
The problem is that it always defaults to the United States, I want to be able to set what country code the dropdown defaults to. Anyone know how I do this?
I've tried adding "DEFAULT": "United Kingdom", to the start of the JSON however, just just adds another selection to the list.
Have you tried moving "GB": "United Kingdom" in front of the list and removing "DEFAULT": "Country/Region" ?
Another possibility is to use Java Script to customize the dropdown but would only recommend that if this behavior is not achievable via policy.
Here is a working example where I have updated the policy to use language customization feature in B2C. Basically moved the GB at the top of Country_List item and remove entry for DEFAULT.
{
"LocalizedStrings": [
{
"ElementType": "UxElement",
"ElementId": null,
"StringId": "alternative_text",
"Override": false,
"Value": "I don't have my phone"
},
{
"ElementType": "UxElement",
"ElementId": null,
"StringId": "button_cancel",
"Override": false,
"Value": "Cancel"
},
{
"ElementType": "UxElement",
"ElementId": null,
"StringId": "button_retry",
"Override": false,
"Value": "Retry"
},
{
"ElementType": "UxElement",
"ElementId": null,
"StringId": "button_send_code",
"Override": false,
"Value": "Send Code"
},
{
"ElementType": "UxElement",
"ElementId": null,
"StringId": "button_verify",
"Override": false,
"Value": "Call Me"
},
{
"ElementType": "UxElement",
"ElementId": null,
"StringId": "button_verify_code",
"Override": false,
"Value": "Verify Code"
},
{
"ElementType": "UxElement",
"ElementId": null,
"StringId": "cancel_message",
"Override": false,
"Value": "The user has cancelled multi-factor authentication"
},
{
"ElementType": "UxElement",
"ElementId": null,
"StringId": "code_pattern",
"Override": false,
"Value": "\\d{6}"
},
{
"ElementType": "UxElement",
"ElementId": null,
"StringId": "countryList",
"Override": true,
"Value": "{\"GB\":\"United Kingdom\", \"AF\":\"Afghanistan\",\"AX\":\"Åland Islands\",\"AL\":\"Albania\",\"DZ\":\"Algeria\",\"AS\":\"American Samoa\",\"AD\":\"Andorra\",\"AO\":\"Angola\",\"AI\":\"Anguilla\",\"AQ\":\"Antarctica\",\"AG\":\"Antigua and Barbuda\",\"AR\":\"Argentina\",\"AM\":\"Armenia\",\"AW\":\"Aruba\",\"AU\":\"Australia\",\"AT\":\"Austria\",\"AZ\":\"Azerbaijan\",\"BS\":\"Bahamas\",\"BH\":\"Bahrain\",\"BD\":\"Bangladesh\",\"BB\":\"Barbados\",\"BY\":\"Belarus\",\"BE\":\"Belgium\",\"BZ\":\"Belize\",\"BJ\":\"Benin\",\"BM\":\"Bermuda\",\"BT\":\"Bhutan\",\"BO\":\"Bolivia\",\"BQ\":\"Bonaire\",\"BA\":\"Bosnia and Herzegovina\",\"BW\":\"Botswana\",\"BV\":\"Bouvet Island\",\"BR\":\"Brazil\",\"IO\":\"British Indian Ocean Territory\",\"VG\":\"British Virgin Islands\",\"BN\":\"Brunei\",\"BG\":\"Bulgaria\",\"BF\":\"Burkina Faso\",\"BI\":\"Burundi\",\"CV\":\"Cabo Verde\",\"KH\":\"Cambodia\",\"CM\":\"Cameroon\",\"CA\":\"Canada\",\"KY\":\"Cayman Islands\",\"CF\":\"Central African Republic\",\"TD\":\"Chad\",\"CL\":\"Chile\",\"CN\":\"China\",\"CX\":\"Christmas Island\",\"CC\":\"Cocos (Keeling) Islands\",\"CO\":\"Colombia\",\"KM\":\"Comoros\",\"CG\":\"Congo\",\"CD\":\"Congo (DRC)\",\"CK\":\"Cook Islands\",\"CR\":\"Costa Rica\",\"CI\":\"Côte d’Ivoire\",\"HR\":\"Croatia\",\"CU\":\"Cuba\",\"CW\":\"Curaçao\",\"CY\":\"Cyprus\",\"CZ\":\"Czechia\",\"DK\":\"Denmark\",\"DJ\":\"Djibouti\",\"DM\":\"Dominica\",\"DO\":\"Dominican Republic\",\"EC\":\"Ecuador\",\"EG\":\"Egypt\",\"SV\":\"El Salvador\",\"GQ\":\"Equatorial Guinea\",\"ER\":\"Eritrea\",\"EE\":\"Estonia\",\"SZ\":\"Eswatini\",\"ET\":\"Ethiopia\",\"FK\":\"Falkland Islands\",\"FO\":\"Faroe Islands\",\"FJ\":\"Fiji\",\"FI\":\"Finland\",\"FR\":\"France\",\"GF\":\"French Guiana\",\"PF\":\"French Polynesia\",\"TF\":\"French Southern Territories\",\"GA\":\"Gabon\",\"GM\":\"Gambia\",\"GE\":\"Georgia\",\"DE\":\"Germany\",\"GH\":\"Ghana\",\"GI\":\"Gibraltar\",\"GR\":\"Greece\",\"GL\":\"Greenland\",\"GD\":\"Grenada\",\"GP\":\"Guadeloupe\",\"GU\":\"Guam\",\"GT\":\"Guatemala\",\"GG\":\"Guernsey\",\"GN\":\"Guinea\",\"GW\":\"Guinea-Bissau\",\"GY\":\"Guyana\",\"HT\":\"Haiti\",\"HM\":\"Heard Island and McDonald Islands\",\"HN\":\"Honduras\",\"HK\":\"Hong Kong SAR\",\"HU\":\"Hungary\",\"IS\":\"Iceland\",\"IN\":\"India\",\"ID\":\"Indonesia\",\"IR\":\"Iran\",\"IQ\":\"Iraq\",\"IE\":\"Ireland\",\"IM\":\"Isle of Man\",\"IL\":\"Israel\",\"IT\":\"Italy\",\"JM\":\"Jamaica\",\"JP\":\"Japan\",\"JE\":\"Jersey\",\"JO\":\"Jordan\",\"KZ\":\"Kazakhstan\",\"KE\":\"Kenya\",\"KI\":\"Kiribati\",\"KR\":\"Korea\",\"KW\":\"Kuwait\",\"KG\":\"Kyrgyzstan\",\"LA\":\"Laos\",\"LV\":\"Latvia\",\"LB\":\"Lebanon\",\"LS\":\"Lesotho\",\"LR\":\"Liberia\",\"LY\":\"Libya\",\"LI\":\"Liechtenstein\",\"LT\":\"Lithuania\",\"LU\":\"Luxembourg\",\"MO\":\"Macao SAR\",\"MG\":\"Madagascar\",\"MW\":\"Malawi\",\"MY\":\"Malaysia\",\"MV\":\"Maldives\",\"ML\":\"Mali\",\"MT\":\"Malta\",\"MH\":\"Marshall Islands\",\"MQ\":\"Martinique\",\"MR\":\"Mauritania\",\"MU\":\"Mauritius\",\"YT\":\"Mayotte\",\"MX\":\"Mexico\",\"FM\":\"Micronesia\",\"MD\":\"Moldova\",\"MC\":\"Monaco\",\"MN\":\"Mongolia\",\"ME\":\"Montenegro\",\"MS\":\"Montserrat\",\"MA\":\"Morocco\",\"MZ\":\"Mozambique\",\"MM\":\"Myanmar\",\"NA\":\"Namibia\",\"NR\":\"Nauru\",\"NP\":\"Nepal\",\"NL\":\"Netherlands\",\"NC\":\"New Caledonia\",\"NZ\":\"New Zealand\",\"NI\":\"Nicaragua\",\"NE\":\"Niger\",\"NG\":\"Nigeria\",\"NU\":\"Niue\",\"NF\":\"Norfolk Island\",\"KP\":\"North Korea\",\"MK\":\"North Macedonia\",\"MP\":\"Northern Mariana Islands\",\"NO\":\"Norway\",\"OM\":\"Oman\",\"PK\":\"Pakistan\",\"PW\":\"Palau\",\"PS\":\"Palestinian Authority\",\"PA\":\"Panama\",\"PG\":\"Papua New Guinea\",\"PY\":\"Paraguay\",\"PE\":\"Peru\",\"PH\":\"Philippines\",\"PN\":\"Pitcairn Islands\",\"PL\":\"Poland\",\"PT\":\"Portugal\",\"PR\":\"Puerto Rico\",\"QA\":\"Qatar\",\"RE\":\"Réunion\",\"RO\":\"Romania\",\"RU\":\"Russia\",\"RW\":\"Rwanda\",\"BL\":\"Saint Barthélemy\",\"KN\":\"Saint Kitts and Nevis\",\"LC\":\"Saint Lucia\",\"MF\":\"Saint Martin\",\"PM\":\"Saint Pierre and Miquelon\",\"VC\":\"Saint Vincent and the Grenadines\",\"WS\":\"Samoa\",\"SM\":\"San Marino\",\"ST\":\"São Tomé and Príncipe\",\"SA\":\"Saudi Arabia\",\"SN\":\"Senegal\",\"RS\":\"Serbia\",\"SC\":\"Seychelles\",\"SL\":\"Sierra Leone\",\"SG\":\"Singapore\",\"SX\":\"Sint Maarten\",\"SK\":\"Slovakia\",\"SI\":\"Slovenia\",\"SB\":\"Solomon Islands\",\"SO\":\"Somalia\",\"ZA\":\"South Africa\",\"GS\":\"South Georgia and South Sandwich Islands\",\"SS\":\"South Sudan\",\"ES\":\"Spain\",\"LK\":\"Sri Lanka\",\"SH\":\"St Helena, Ascension, Tristan da Cunha\",\"SD\":\"Sudan\",\"SR\":\"Suriname\",\"SJ\":\"Svalbard\",\"SE\":\"Sweden\",\"CH\":\"Switzerland\",\"SY\":\"Syria\",\"TW\":\"Taiwan\",\"TJ\":\"Tajikistan\",\"TZ\":\"Tanzania\",\"TH\":\"Thailand\",\"TL\":\"Timor-Leste\",\"TG\":\"Togo\",\"TK\":\"Tokelau\",\"TO\":\"Tonga\",\"TT\":\"Trinidad and Tobago\",\"TN\":\"Tunisia\",\"TR\":\"Turkey\",\"TM\":\"Turkmenistan\",\"TC\":\"Turks and Caicos Islands\",\"TV\":\"Tuvalu\",\"UM\":\"U.S. Outlying Islands\",\"VI\":\"U.S. Virgin Islands\",\"UG\":\"Uganda\",\"UA\":\"Ukraine\",\"AE\":\"United Arab Emirates\",\"US\":\"United States\",\"UY\":\"Uruguay\",\"UZ\":\"Uzbekistan\",\"VU\":\"Vanuatu\",\"VA\":\"Vatican City\",\"VE\":\"Venezuela\",\"VN\":\"Vietnam\",\"WF\":\"Wallis and Futuna\",\"YE\":\"Yemen\",\"ZM\":\"Zambia\",\"ZW\":\"Zimbabwe\"}"
},
{
"ElementType": "UxElement",
"ElementId": null,
"StringId": "country_code_input_placeholder_text",
"Override": false,
"Value": "Country or region"
},
{
"ElementType": "UxElement",
"ElementId": null,
"StringId": "country_code_label",
"Override": false,
"Value": "Country Code"
},
{
"ElementType": "UxElement",
"ElementId": null,
"StringId": "enter_code_text_intro",
"Override": false,
"Value": "Enter your verification code below, or "
},
{
"ElementType": "UxElement",
"ElementId": null,
"StringId": "error_448",
"Override": false,
"Value": "The phone number you provided is unreachable."
},
{
"ElementType": "UxElement",
"ElementId": null,
"StringId": "error_449",
"Override": false,
"Value": "User has exceeded the number of retry attempts."
},
{
"ElementType": "UxElement",
"ElementId": null,
"StringId": "error_incorrect_code",
"Override": false,
"Value": "The verification code you have entered does not match our records. Please try again, or request a new code."
},
{
"ElementType": "UxElement",
"ElementId": null,
"StringId": "error_tryagain",
"Override": false,
"Value": "The phone number you provided is busy or unavailable. Please check the number and try again."
},
{
"ElementType": "UxElement",
"ElementId": null,
"StringId": "intro_entry_mixed",
"Override": false,
"Value": "Enter a number below that we can send a code via SMS or phone to authenticate you."
},
{
"ElementType": "UxElement",
"ElementId": null,
"StringId": "intro_entry_phone",
"Override": false,
"Value": "Enter a number below that we can phone to authenticate you."
},
{
"ElementType": "UxElement",
"ElementId": null,
"StringId": "intro_entry_sms",
"Override": false,
"Value": "Enter a number below that we can send a code via SMS to authenticate you."
},
{
"ElementType": "UxElement",
"ElementId": null,
"StringId": "intro_mixed",
"Override": false,
"Value": "We have the following number on record for you. We can send a code via SMS or phone to authenticate you."
},
{
"ElementType": "UxElement",
"ElementId": null,
"StringId": "intro_mixed_p",
"Override": false,
"Value": "We have the following numbers on record for you. Choose a number that we can phone or send a code via SMS to authenticate you."
},
{
"ElementType": "UxElement",
"ElementId": null,
"StringId": "intro_phone",
"Override": false,
"Value": "We have the following number on record for you. We will phone to authenticate you."
},
{
"ElementType": "UxElement",
"ElementId": null,
"StringId": "intro_phone_p",
"Override": false,
"Value": "We have the following numbers on record for you. Choose a number that we can phone to authenticate you."
},
{
"ElementType": "UxElement",
"ElementId": null,
"StringId": "intro_sms",
"Override": false,
"Value": "We have the following number on record for you. We will send a code via SMS to authenticate you."
},
{
"ElementType": "UxElement",
"ElementId": null,
"StringId": "intro_sms_p",
"Override": false,
"Value": "We have the following numbers on record for you. Choose a number that we can send a code via SMS to authenticate you."
},
{
"ElementType": "UxElement",
"ElementId": null,
"StringId": "invalid_code",
"Override": false,
"Value": "Please enter the 6 digit code you received"
},
{
"ElementType": "UxElement",
"ElementId": null,
"StringId": "invalid_number",
"Override": false,
"Value": "Please enter a valid phone number"
},
{
"ElementType": "UxElement",
"ElementId": null,
"StringId": "local_number_input_placeholder_text",
"Override": false,
"Value": "Phone number"
},
{
"ElementType": "UxElement",
"ElementId": null,
"StringId": "number_label",
"Override": false,
"Value": "Phone Number"
},
{
"ElementType": "UxElement",
"ElementId": null,
"StringId": "number_pattern",
"Override": false,
"Value": "^\\+(?:[0-9][\\x20-]?){6,14}[0-9]$"
},
{
"ElementType": "UxElement",
"ElementId": null,
"StringId": "requiredField_code",
"Override": false,
"Value": "Please enter the verification code you received"
},
{
"ElementType": "UxElement",
"ElementId": null,
"StringId": "requiredField_countryCode",
"Override": false,
"Value": "Please select your country code"
},
{
"ElementType": "UxElement",
"ElementId": null,
"StringId": "requiredField_number",
"Override": false,
"Value": "Please enter your phone number"
},
{
"ElementType": "UxElement",
"ElementId": null,
"StringId": "text_button_send_second_code",
"Override": false,
"Value": "send a new code"
},
{
"ElementType": "UxElement",
"ElementId": null,
"StringId": "verification_code_input_placeholder_text",
"Override": false,
"Value": "Verification code"
}
]
}
Related
Stripe Webhook doesnt capture product data/meta data
I want to use stripe payment link system, The payment method can be Card/Wallet I dont want to use checkout button system, since the payment isnt dynamic Once the payment is successful (auto subscription or manual checkout) I need to send the activation code to the users email. The next year when the charge is auto debited from card I need to generate the activation key again and send the same for the next year. I see there are many events in the Stripe payment webhooks, I have used charge.succeeded event listener and it does send me the object (pasted below). I have one issue here. If I rely on this charge.succeeded object I find no information on which product the purchase is made. There are many products in my system The response have user supplied email but there is no way I product details in the charge.succeeded object. I have supplied the metadata info in the payment link page as below. I have also supplied the metadata in products as well as metadata in the every pricings I use this link - https://buy.stripe.com/test_28o3cn6hC5bgdoIcMM Test card number: 4242 4242 4242 4242 and any dates and CVV number would work. Webhook responses are captured here - https://docs.google.com/spreadsheets/d/1RjnFnjHvs9ca8tIPoRiNHFUph_npm5pVK2S15wVllzI/edit?usp=sharing Any help is greatly appreciated { "id": "evt_1KWzCbHpIo9Nhh5aYEv7XlY8", "object": "event", "api_version": "2017-12-14", "created": 1645777861, "data": { "object": { "id": "ch_1KWzCaHpIo9Nhh5atK09dpaO", "object": "charge", "amount": 100, "amount_captured": 100, "amount_refunded": 0, "application": null, "application_fee": null, "application_fee_amount": null, "balance_transaction": "txn_1KWzCaHpIo9Nhh5anEebLs4X", "billing_details": { "address": { "city": null, "country": "IN", "line1": null, "line2": null, "postal_code": null, "state": null }, "email": "te...#email.com", "name": "CARDNAME", "phone": null }, "calculated_statement_descriptor": "XXXXXXXXXXX", "captured": true, "created": 1645777860, "currency": "inr", "customer": "cus_LDQ2DBhL2VkPOH", "description": "Subscription creation", "destination": null, "dispute": null, "disputed": false, "failure_code": null, "failure_message": null, "fraud_details": { }, "invoice": "in_1KWzCYHpIo9Nhh5ammBkFvU1", "livemode": false, "metadata": { }, "on_behalf_of": null, "order": null, "outcome": { "network_status": "approved_by_network", "reason": null, "risk_level": "normal", "risk_score": 58, "seller_message": "Payment complete.", "type": "authorized" }, "paid": true, "payment_intent": "pi_1KWzCYHpIo9Nhh5aj6Xgl3tS", "payment_method": "pm_1KWzCXHpIo9Nhh5aADMKyWPc", "payment_method_details": { "card": { "brand": "visa", "checks": { "address_line1_check": null, "address_postal_code_check": null, "cvc_check": "pass" }, "country": "US", "exp_month": 2, "exp_year": 2022, "fingerprint": "MxtsbEBU2BmJbOn4", "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_1BhpF1HpIo9Nhh5a/ch_1KWzCaHpIo9Nhh5atK09dpaO/rcpt_LDQ2FDPK6fwsEyBOISiDCItSv8JeNbl", "refunded": false, "refunds": { "object": "list", "data": [ ], "has_more": false, "total_count": 0, "url": "/v1/charges/ch_1KWzCaHpIo9Nhh5atK09dpaO/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 } }, "livemode": false, "pending_webhooks": 1, "request": { "id": "req_QzrolSFU0OA7D7", "idempotency_key": "228c5670-85ea-4047-9f9f-9d1e519ffc2c" }, "type": "charge.succeeded" }
a Checkout Session will be generated under the hood when your customer opens a Payment Link. Therefore, you should listen to the checkout.session.completed events, and get the product data from the line_items property of the Checkout Session object.
I have had a similar problem when using a webhook solution in Stripe. Although I had defined meta data in the product it was not shipped, i.e. metadata in JSON was empty. In my case the solution has been to use the meta data of the price instead (which is defined within the product). For this, just click price section on product page on Stripe website... ...and define meta data on the price page. There is another interesting post https://stackoverflow.com/a/69117489/10849985 showing that it is event-depending if meta data is provided or not, i.e. some events linked to the webhook inlcude it, others not.
For me the issue was that I was merging the price and products objects together which was overwriting the metadata on the product with the empty metadata of the price object. To fix this I just made sure the metadata was receiving the values from the product obj const product = await stripe.products.list(); const price = await stripe.prices.list(); const productData = product.data.map((productData, index) => { return { ...productData, ...price.data[index], quantity: 1, addedToCart: false, metadata: productData.metadata }; });
How to get Product and Quantity information from payment_intent.succeeded event via Webhooks in Stripe?
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);
Stripe events: How to capture the product that was successfully paid with Stripe events and a webhook?
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.
How to update a subscription (adding a pricing plan) by Stripe in Node.js?
I'm building a web app which sells several products, each product corresponds to a pricing plan. All the payment system is managed by Stripe. It happens very often that a user has a subscription that contains Product A (i.e., one pricing plan), and then he wants to add Product B (i.e., another pricing plan) to the same subscription. I want to know how to achieve this by APIs of Stripe. There is a webpage of Stripe to update a subscription (e.g., https://dashboard.stripe.com/test/subscriptions/sub_H4pQGW8nnc80vF/edit where sub_H4pQGW8nnc80vF is the subscription ID). Let's assume this customer already has 1 Verificator in the subscription, and then he wants to add 1 Pretty Formula to the same subscription. I do this update via the website. Here is the log: Here is the full Request POST body: { "items": { "0": { "billing_thresholds": "", "deleted": "false", "id": "si_H4pQal4ZxGzLbW", "quantity": "1", "tax_rates": "" }, "1": { "billing_thresholds": "", "plan": "plan_Gz6i9yPVIjrDPX", "deleted": "false", "quantity": "1" } }, "off_session": "true", "prorate": "true", "cancel_at": "", "days_until_due": "30", "default_tax_rates": "", "collection_method": "send_invoice", "billing_thresholds": "", "enable_incomplete_payments": "false", "invoice_settings": { "description": "", "send_hosted_payment_email": "true", "supported_payment_methods": { "ach_credit_transfer": "false", "au_becs_debit": "false", "bancontact": "false", "card": "true", "fpx": "false", "giropay": "false", "ideal": "false", "jp_credit_transfer": "false", "paper_check": "false", "sepa_credit_transfer": "false", "sofort": "false" }, "custom_fields": "", "footer": "" }, "default_payment_method": "", "default_source": "" } Here is the full Response body: { "id": "sub_H4pQGW8nnc80vF", "object": "subscription", "application_fee_percent": null, "billing_cycle_anchor": 1586597833, "billing_thresholds": null, "cancel_at": null, "cancel_at_period_end": false, "canceled_at": null, "collection_method": "send_invoice", "created": 1586597833, "current_period_end": 1589189833, "current_period_start": 1586597833, "customer": "5e575130651c5721d808d25b", "customer_email": "sdtikply#gmail.com", "customer_name": "Thomas Joseph", "days_until_due": 30, "default_payment_method": null, "default_source": null, "default_tax_rates": [ ], "discount": null, "ended_at": null, "invoice_settings": { "send_hosted_payment_email": true, "supported_payment_methods": { "ach_credit_transfer": false, "au_becs_debit": false, "bancontact": false, "card": true, "fpx": false, "giropay": false, "ideal": false, "jp_credit_transfer": false, "paper_check": false, "sepa_credit_transfer": false, "sofort": false } }, "items": { "object": "list", "data": [ { "id": "si_H4pQal4ZxGzLbW", "object": "subscription_item", "billing_thresholds": null, "created": 1586597833, "metadata": { }, "plan": { "id": "plan_Ga6n9yMYCDnHCu", "object": "plan", "active": true, "aggregate_usage": null, "amount": 500, "amount_decimal": "500", "billing_scheme": "per_unit", "created": 1579512574, "currency": "usd", "interval": "month", "interval_count": 1, "livemode": false, "metadata": { }, "name": "Verificator", "nickname": "Verificator", "owning_merchant": "acct_1CiOQBEV4K2GahYL", "owning_merchant_info": "acct_1CiOQBEV4K2GahYL", "product": "prod_Ga6mVdA8KXyZ8I", "tiers": null, "tiers_mode": null, "transform_usage": null, "trial_period_days": null, "usage_type": "licensed" }, "quantity": 1, "subscription": "sub_H4pQGW8nnc80vF", "tax_rates": [ ] }, { "id": "si_H82ES9BdIKZCNG", "object": "subscription_item", "billing_thresholds": null, "created": 1587337381, "metadata": { }, "plan": { "id": "plan_Gz6i9yPVIjrDPX", "object": "plan", "active": true, "aggregate_usage": null, "amount": 500, "amount_decimal": "500", "billing_scheme": "per_unit", "created": 1585278262, "currency": "usd", "interval": "month", "interval_count": 1, "livemode": false, "metadata": { }, "name": "Pretty Formula", "nickname": "Pretty Formula", "owning_merchant": "acct_1CiOQBEV4K2GahYL", "owning_merchant_info": "acct_1CiOQBEV4K2GahYL", "product": "prod_GxqkRFdI08DvyR", "tiers": null, "tiers_mode": null, "transform_usage": null, "trial_period_days": null, "usage_type": "licensed" }, "quantity": 1, "subscription": "sub_H4pQGW8nnc80vF", "tax_rates": [ ] } ], "has_more": false, "total_count": 2, "url": "/v1/subscription_items?subscription=sub_H4pQGW8nnc80vF" }, "latest_invoice": "in_1GWfm1EV4K2GahYLlmtUEISo", "livemode": false, "metadata": { }, "next_pending_invoice_item_invoice": null, "owning_merchant": "acct_1CiOQBEV4K2GahYL", "owning_merchant_info": "acct_1CiOQBEV4K2GahYL", "pause_collection": null, "pending_invoice_item_interval": null, "pending_setup_intent": null, "pending_update": null, "plan": null, "quantity": null, "schedule": null, "start_date": 1586597833, "status": "active", "tax_percent": null, "trial_end": null, "trial_start": null } So my question is, how can I code in my backend (Node.js) to achieve exactly the same thing?
You just need to use the Update Subscription API and provide the items portion just as you're seeing above: "items": { "0": { "billing_thresholds": "", "deleted": "false", "id": "si_H4pQal4ZxGzLbW", "quantity": "1", "tax_rates": "" }, "1": { "billing_thresholds": "", "plan": "plan_Gz6i9yPVIjrDPX", "deleted": "false", "quantity": "1" } }, The first item is the existing Subscription Item (si_), and the second one is the new one you want to add.
How to get customer id from a charge response of stripe?
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!