Stripe Charges vs Orders clarifications - stripe-payments

I'm implementing a payment platform with Stripe.
For a single payment I create a Charge.
It works, no problem, but I also need to apply a tax fee to the product which is defined in the Order object.
Does that mean I need to replace Charges with Orders somehow or is Order a parent object for the charge? If so how do I link those 2 together? Or there is a way to apply tax fees to charges I didn't see?
Thanks
EDIT:
public async Task<StripeCharge> CreateStripeChargeAsync(StripeProduct product, StripeSku sku, StripeCustomer customer, string orderId)
{
var charge = await _chargeService.CreateAsync(new StripeChargeCreateOptions
{
Amount = sku.Price,
Currency = sku.Currency,
Description = product.Description,
CustomerId = customer.Id,
SourceTokenOrExistingSourceId = customer.DefaultSourceId,
ReceiptEmail = customer.Email,
Metadata = new Dictionary<string, string>
{
{ "OrderId", orderId }
}
});
return charge;
}

You can add a tax to your order by creating an order item with the type set to tax, for example
Stripe\OrderItem JSON: {
"object": "order_item",
"type": "tax",
"amount": 1234,
"currency": "usd",
"description": "Tax",
"parent": "null",
"quantity": null
}
Attach this to your orders item list. I can't really give a code example since you didn't mention a language but your order object would look like...
Stripe\Order JSON: {
"id": "or_1CZfbeCAQSQw5dVw1BSP63dZ",
"object": "order",
"amount": 1500,
"amount_returned": null,
"application": null,
"application_fee": null,
"charge": null,
"created": 1528206830,
"currency": "usd",
"customer": null,
"email": null,
"items": [
{
"object": "order_item",
"amount": 10000,
"currency": "usd",
"description": "My Product name",
"parent": "sk_1BQuNoCAQSQw5dVwjf30OpxH",
"quantity": null,
"type": "sku"
},
"object": "order_item",
"type": "tax",
"amount": 1234,
"currency": "usd",
"description": "Tax",
"parent": "null",
"quantity": null
], etc...
You can use the same idea for adding shipping costs or even applying discounts by using type="shipping" or "discount".
Notice the order object contains a charge field. Once the order is paid this is populated with the charge id. No need to replace charges with orders, Stripe will do this update for you linking the payment to the order.
See the API Docs for more information.

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 can I pay Stripe with saved credit card?

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!
}
}
});

How to change total according to item quantity

I've got a paypal payment at my site. Looks like this:
app.post('/pay', (req, res) => {
console.log(req.body);
const create_payment_json = {
"intent": "sale",
"payer": {
"payment_method": "paypal"
},
"redirect_urls": {
"return_url": "http://localhost:1234/success",
"cancel_url": "http://localhost:1234/cancel"
},
"transactions": [{
"item_list": {
"items": [{
"name": req.body.user_name,
"sku": "001",
"price": "25",
"currency": "USD",
"quantity": req.body.persons_count
}]
},
"amount": {
"currency": "USD",
"total": "25"
},
"description": req.body.user_name + " with email " + req.body.email + " just ordered " + req.body.persons_count + " places"
}]
};
paypal.payment.create(create_payment_json, function (error, payment) {
if (error) {
throw error;
} else {
res.send('on my way');
console.log(payment);
}
});
})
If I change total field in amount object (this is what I wanna do) I get a 400 response (bad request). How can I make a payment like this:
"amount":{
"total": req.body.persons_count * 2
}
Where req.body.persons_count variable is a variable I get from a post request from one of the forms I have earlier.
Fighting with that code showed, that both price and total values have to be equal however I want the price of a single item be different from the total amount I wanna get. Huge thanks!
By the way quantity value has to be equal to 1. In all other cases app crahes.
Total amount should be sum of items amount, shipping amount, tax and other fees.
In your case, total : Item price * Item quantity
sample,
"transactions": [
{
"amount": {
"currency": "USD",
"total": "20",
"details": {
"shipping": "1.20",
"tax": "1.30",
"subtotal": "17.50"
}
},
"description": "Payment description",
"invoice_number": "1231123524",
"custom": "custom data",
"item_list": {
"items": [
{
"name": "Ground Coffee 40 oz",
"currency": "USD",
"quantity": 1,
"price": "7.50"
},
{
"name": "Granola bars",
"currency": "USD",
"quantity": 5,
"price": "2"
}
]
}
}
]
You can also refer https://developer.paypal.com/docs/api/payments/v1/#payment_create

How do I send shipping info to Paypal via Node.js with paypal-rest-sdk module?

I was wondering how do I send shipment info to Paypal so that when the user gets redirected to Paypal for payment, the user doesn't have to fill out Shipping information again. The user already filled out shipping information and the data is stored in my database. The issue is that the shipping info stored in the database will more than likely not be the same address as the person making the order. The user will more than likely be using another shipping address, not their own. Now I can operate just fine this way but I was thinking: If I dont have the correct shipping address stored on Paypal's records, the customer could easily say I never shipped their product and demand a refund. At this point, I would only have the tracking # to the address stored on my database, not on Paypal's records, thus making me vulnerable to these kind of paypal scams. Correct me if Im wrong anybody.
Below is Paypals example on what data is sent to them during ordering process:
https://github.com/paypal/PayPal-node-SDK/blob/master/samples/order/create.js
var create_payment_json = {
"intent": "order",
"payer": {
"payment_method": "paypal"
},
"redirect_urls": {
"return_url": "http://return.url",
"cancel_url": "http://cancel.url"
},
"transactions": [{
"item_list": {
"items": [{
"name": "item",
"sku": "item",
"price": "1.00",
"currency": "USD",
"quantity": 1
}]
},
"amount": {
"currency": "USD",
"total": "1.00"
},
"description": "This is the payment description."
}]
};
I added a shipping object, see the sample below.
var create_payment_json = {
"intent": "sale",
"payer": {
"payment_method": "paypal"
},
"redirect_urls": {
"return_url": "https://website.com/thankyou",
"cancel_url": "https://website.com"
},
"transactions": [{
"item_list": {
"items": [{
"name": name,
"sku": "item",
"price": total_price,
"currency": "USD",
"quantity": 1
}]
},
"shipping_address": {
"recipient_name":recipient_name,
"line1":line_1,
"city": city,
"state":state,
"postal_code":zipcode,
"country_code":"US"
},
"amount": {
"currency": "USD",
"total": total_price
},
"description": description
}]
};
But I get this error:
"response": {
"name":"MALFORMED REQUEST",
"message":"Incoming JSON request",
"information_link":"https://developer.paypal.com/webapps/developer/docs/api/#MALFORMED_REQUEST",
"debug_id":"cb2ecc213218b",
"httpStatusCode":400
},
"httpStatusCode":400
}
I didn't just make this up the shipping object. On a successful Paypal payment, PayPal returns a transaction object that includes the shipping address object. I copied exactly the way I saw it, assuming I could send the shipping info to Paypal.
How can I send shipping info to Paypal via REST API / node.js when creating a Paypal order. Remember, when you checkout at any store and login to Paypal, the shipping address is automatically your shipping address that you set with Paypal. How do I change it via API? If you cant, how do stores typically handle these kinds of situations? For example, buy a gift for a friend. Thanks for your time, your help is much appreciated.
You have the right approach. The shipping_address object belongs inside the item_list next to the items array.
An example here:
var create_payment_json = {
"intent": "sale",
"payer": {
"payment_method": "paypal"
},
"redirect_urls": {
"return_url": "https://website.com/thankyou",
"cancel_url": "https://website.com"
},
"transactions": [{
"item_list": {
"shipping_address": {
"recipient_name":recipient_name,
"line1":line_1,
"city": city,
"state":state,
"postal_code":zipcode,
"country_code":"US"
},
"items": [{
"name": name,
"sku": "item",
"price": total_price,
"currency": "USD",
"quantity": 1
}]
},
"amount": {
"currency": "USD",
"total": total_price
},
"description": description
}]
};

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!

Resources