I have a Stripe checkout session that triggers a webhook when a transaction is completed. The webhook retrieves a list of line_items that were purchased and is supposed to identify the products in the database so that it can set the status in the database to 'sold'.
// api/checkout
const session = await stripe.checkout.sessions.create({
payment_method_types: ['card'],
mode: 'payment',
line_items: req.body.itemsIDs.map(itemId => {
const storeItem = items.find(i => i.id === itemId);
return {
price_data: {
currency: 'usd',
product_data: {
name: "My Product",
images: [storeItem.preview]
// Can I save the itemId here somehow? I need it in the webhook later to identify the product in the db
},
unit_amount: storeItem.price * 100
},
quantity: 1
}
}),
success_url: `${process.env.SERVER_URL}/success`,
cancel_url: `${process.env.SERVER_URL}`
});
In the webhook, I have access to the session's purchased items (line_items), but I have no way of finding this item in the database. Here's what the line_item object looks like:
{
id: 'li_1M4iB1DjiBaV2GrrFmb0bTVD', // this is NOT the MongoDB Id that I need, it's a Stripe-generated id
object: 'item',
amount_discount: 0,
amount_subtotal: 100,
amount_tax: 0,
amount_total: 100,
currency: 'usd',
description: 'My Product',
price: {
id: 'price_1M4iB1DjiBaV2Grr6SnKEWdC',
object: 'price',
active: false,
billing_scheme: 'per_unit',
created: 1668591903,
currency: 'usd',
custom_unit_amount: null,
livemode: false,
lookup_key: null,
metadata: {},
nickname: null,
product: 'prod_Mo4ql746Hw4pck',
recurring: null,
tax_behavior: 'unspecified',
tiers_mode: null,
transform_quantity: null,
type: 'one_time',
unit_amount: 100,
unit_amount_decimal: '100'
},
quantity: 1
}
When passing line_items.price_data [1] to the creation request of the Checkout Session, you are creating a new price on each purchase. You should instead use line_items.price [2] with existing Stripe Prices[3] and Product[4]. You can set your own Ids in the Metadata of both prices [5] and products[6].
After completing the Checkout Session and when receiving the Webhook, you’ll get the Prices with its metadata that contains your database related Ids, as you have access to the session's purchased items (line_items).
[1] https://stripe.com/docs/api/checkout/sessions/create#create_checkout_session-line_items-price_data
[2] https://stripe.com/docs/api/checkout/sessions/create#create_checkout_session-line_items-price
[3] https://stripe.com/docs/api/products/create
[4] https://stripe.com/docs/api/prices/create
[5] https://stripe.com/docs/api/prices/create#create_price-metadata
[6] https://stripe.com/docs/api/products/create#create_product-metadata
[7] https://stripe.com/docs/api/events/types#event_types-checkout.session.completed
Related
I have a problem.
I fetch data with 2 parameters.
user_id and movie_channel
so a user has multiple movie channels like 1,2 or 3.
I fetch now a query with this params:
user_id: 1, movie_channel: 1
obj:
return {
user: {
user_id: 1,
username: 'assa',
is_follow: false
},
movie_channel: 1,
movies: []
}
then I get a list of movies from this channel and you get users information.
So anyone select now movie_channel 2, then I fetch again and get the obj with different movies.
in the header he can follow a person. (he is current now in movie channel 2)
he can now change the movie_channel to 1 and then I get the cached data. But now user is not followed because he followed in the channel 2. the cache shows the old obj.
how can I change all cached data where only the param is user_id ?
useGetProfileData: builder.query<IProfilePageData, { user_id: number; movie_channel?: number; }>({
query: (data) => ({
url: '/profile_data',
method: 'POST',
body: data
}),
}),
followUser: builder.mutation<void, { user_id: number; follower_id: number; movie_channel?: number; }>({
query: (data) => ({
url: '/follow_user',
method: 'POST',
body: data
}),
async onQueryStarted({ user_id, follower_id, movie_channel }, { dispatch, queryFulfilled }){
const patchResult = dispatch(
ProfileApi.util.updateQueryData('useGetProfileData', { user_id, movie_channel }, (draft) => {
return {
...draft,
user: {
...draft.user,
is_follow: !draft.user.is_follow
}
}
})
);
try {
await queryFulfilled;
} catch {
patchResult.undo();
}
}
}),
is there any way to add a product to the cart with its own data that will not be displayed in the checkout, but only in the administration under a specific order?
I found only: product_id quantity variation_id variations
The problem is that I have a native woocommerce eshop, but I have the configurator developed in React.
After complete filling (by the customer), the configurator should send all its inputs, checkboxes and choices as custom data to the basket, where after the subsequent order, these data would be visible in the administration.
Something like this:
export const addToCart = (productId, qty, customData) => {
const addOrViewCartConfig = getApiCartConfig();
setLoading(true);
axios.post(
CART_ENDPOINT,
{
product_id: productId,
quantity: qty,
//customData
},
addOrViewCartConfig
);
};
My customData:
{
obj1: {
font: "arial",
text: "Some string.",
size: 12,
is_edited: false
},
obj2: {
font: "sans-serif",
text: "Some string.",
size: 15,
is_edited: true,
},
}
Please, is there any solution for my problem?
Thanks
I am trying to collect and transfer payments using firebase cloud functions in my react native app. I'm using the stripe.accounts.create and stripe.paymentIntents.create functions as well as library axios. I'm really not too sure how to create the connect account and pass the account id created into the payment intent method. I get the following error in the firebase logs 'code: 'StripeInvalidRequestError: Can only apply an application_fee_amount when the PaymentIntent is attempting a direct payment (using an OAuth key or Stripe-Account header) or destination payment (using transfer_data[destination])'
when trying to run my code below. Can someone please assist? I don't think the connectAcc.id is null since I can see it in my stripe dashboard logs in the response body where the account is created:
Response body
{
"id": "acct_**********U5",
"object": "account",
"business_profile": {
"mcc": "5734",
"name": null,
"product_description": null,
"support_address": null,
"support_email": null,
"support_phone": null,
"support_url": null,
index.js file
const stripe = require('stripe')('**SK_LIVE**');
exports.payWithStripe = functions.https.onRequest((request, response) => {
const connectAcc = stripe.accounts.create({
type: 'custom',
email: 'name#gmail.com',
country: 'GB',
business_type: 'individual',
business_profile: {
mcc: '5734',
url: 'site.com',
},
individual: {
first_name: 'First',
last_name: 'Last',
dob : {
day: 1,
month: 10,
year: 1990
},
email: 'name#gmail.com',
phone: '+44xxxxxxx',
address: {
city: 'city',
country: 'GB',
line1: '1',
line2: 'Street Rd',
postal_code: 'XXX XXX'
}
},
tos_acceptance: {
date: Math.floor(Date.now() / 1000),
ip: request.connection.remoteAddress,
},
capabilities: {
card_payments: {requested: true},
transfers: {requested: true},
},
external_account: {
object: 'bank_account',
country: 'GB',
currency: 'gbp',
account_number: 'xxxxx',
routing_number: 'xxxxx',
accounter_holder_name: 'First Last',
account_holder_type: 'individual',
}
})
stripe.paymentIntents.create({
amount: request.body.amount,
currency: request.body.currency,
payment_method_types: ['card'],
payment_method: request.body.payment_method.id,
application_fee_amount: 20,
on_behalf_of: connectAcc.id,
transfer_data: {
destination: connectAcc.id,
},
confirm: true,
description: 'UniHome'
}
).then((charge) => {
response.send(charge);
})
.catch(err =>{
console.log(err);
})
});
Thanks.
connectAcc is not an account object — it's a Promise. That's what Stripe's SDK returns.
You'd have to resolve the Promise first, like:
let connectAcc = await stripe.accounts.create(...); let id = connectAcc.id or stripe.accounts.create(...).then(function(acct){let id = acct.id;} )
I'm developing flutter app with FireStore.
I have declared a variable 'total' and List of objects to be filled with some data from collection called 'cart', then upload these data to collection called 'orders'
This code grab the data from collection 'cart':
void submitOrder() async {
List<CartItem> products =[];
double total =0;
CartItem temp= CartItem(customerId:'' ,itemId:'' ,id: '', title: '', quantity:0, price: 0);
DatabaseService().cartCollection.getDocuments().then(
(snapshot) => snapshot.documents.forEach((doc) {
temp = CartItem(
customerId: user.uid,
itemId: doc.data['itemId'].toString(),
id: doc.documentID.toString(),
title: doc.data['itemName'].toString(),
quantity: int.tryParse(doc.data['quantity'].toString()) ,
price: double.tryParse( doc.data['price'].toString()));
total += temp.quantity*temp.price;
print(total); /// This print shows accurate total
products.add(temp);
}
),
);
// Send data to setOrderData in db class to set new doc in order collection
DatabaseService().setOrderData(products, total, user.uid, branchId, 'open');
}
method setOrderData in DB class:
// Add Order
Future setOrderData(List<CartItem> cartProducts, double total, String customerId, String branchId, String status ) async {
final timestamp = DateTime.now();
return await orderCollection.document(uid).setData(
{
'customerId': customerId,
'branchId': branchId,
'status': status ,
'amount': total.toString(), //
'dateTime': timestamp.toIso8601String(),
'products': cartProducts.map((cp) => {
'id': cp.id,
'title': cp.title,
'quantity': cp.quantity,
'price': cp.price,
}).toList(),
});
}
data in FireStore shows that products and total are null?
Link to image:
If anyone can help me out I'd be grateful.
I highly recommend using a code formatter on your code, as it makes it much more likely that people can spot problems like the one you're having. When I reformat the first code block you shared, it becomes:
void submitOrder() async {
List<CartItem> products = [];
double total = 0;
CartItem temp = CartItem(
customerId: '', itemId: '', id: '', title: '', quantity: 0, price: 0);
DatabaseService().cartCollection.getDocuments().then(
(snapshot) => snapshot.documents.forEach((doc) {
temp = CartItem(
customerId: user.uid,
itemId: doc.data['itemId'].toString(),
id: doc.documentID.toString(),
title: doc.data['itemName'].toString(),
quantity: int.tryParse(doc.data['quantity'].toString()),
price: double.tryParse(doc.data['price'].toString()));
total += temp.quantity * temp.price;
print(total);
/// This print shows accurate total
products.add(temp);
}),
);
// Send data to setOrderData in db class to set new doc in order collection
DatabaseService().setOrderData(products, total, user.uid, branchId, 'open');
}
An in this format it's immediately clear to me that you're calling setOrderData before any of the products.add(temp) calls have happened.
This is because data is loaded from Firestore asynchronously. Since this may take some time, your main code (including the return) continues so the UI is not blocked. Then when the data is available, your then callback is invoked.
This means that any code that needs the data from Firestore needs to be inside the then callback. So in your case, the solution could be as simple as moving the call to setOrderData to inside the then:
void submitOrder() async {
List<CartItem> products = [];
double total = 0;
CartItem temp = CartItem(
customerId: '', itemId: '', id: '', title: '', quantity: 0, price: 0);
DatabaseService().cartCollection.getDocuments().then(
(snapshot) => snapshot.documents.forEach((doc) {
temp = CartItem(
customerId: user.uid,
itemId: doc.data['itemId'].toString(),
id: doc.documentID.toString(),
title: doc.data['itemName'].toString(),
quantity: int.tryParse(doc.data['quantity'].toString()),
price: double.tryParse(doc.data['price'].toString()));
total += temp.quantity * temp.price;
print(total);
/// This print shows accurate total
products.add(temp);
}),
// Send data to setOrderData in db class to set new doc in order collection
DatabaseService().setOrderData(products, total, user.uid, branchId, 'open');
);
}
So the things to take away from this:
Always format your code, as it makes it easier for you and others to understand the flow and find problems.
Data is loaded from Firestore (and most cloud APIs) asynchronously, and you can only use the data inside the then() callback, or by using await.
The best Practice to solve such an error is to use try and then catch with the await for each function like the below code
void submitOrder() async {
List<CartItem> products = [];
double total = 0;
CartItem temp = CartItem(
customerId: '', itemId: '', id: '', title: '', quantity: 0, price: 0);
try {
await DatabaseService().cartCollection.getDocuments().then(
(snapshot) => snapshot.documents.forEach((doc) {
temp = CartItem(
customerId: user.uid,
itemId: doc.data['itemId'].toString(),
id: doc.documentID.toString(),
title: doc.data['itemName'].toString(),
quantity: int.tryParse(doc.data['quantity'].toString()),
price: double.tryParse(doc.data['price'].toString()));
total += temp.quantity * temp.price;
print(total);
/// This print shows accurate total
products.add(temp);
}),
);
// Send data to setOrderData in db class to set new doc in order collection
await DatabaseService()
.setOrderData(products, total, user.uid, branchId, 'open');
} catch (error) {
print(error);
}
}
I am using AWS Amplify and want to add new data into AWS AppSync. Below is the schema that I have:
type Product
#model
#auth(rules: [{ allow: owner, ownerField: "userId" }, { allow: groups, groups: ["admin"] }]) {
id: Int
userId: ID
owner: String
title: String
}
type UpsellRule
#model
#auth(rules: [{ allow: owner, ownerField: "userId" }, { allow: groups, groups: ["admin"] }]) {
id: String
userId: ID
owner: String
name: String
subscriptionProducts: [Product]
upsellProducts: [Product]
}
Based on that schema, this is the mutation that I have to add a new item:
const createUpsellRule = `mutation CreateUpsellRule($input: CreateUpsellRuleInput!) {
createUpsellRule(input: $input) {
id
userId
owner
name
subscriptionProducts {
id
userId
owner
title
}
upsellProducts {
id
userId
owner
title
}
}
}
This is an example of the payload that I sent:
{
id: '<a-unique-id>',
userId: '<a-unique-userId>',
owner: '<an-owner>',
subscriptionProducts: [{
userId: '89217803-5fff-44d9-93af-e72b05012813',
owner: '<an-owner>',
id: 4448795164753,
name: 'Product 10'
},
{
userId: '89217803-5fff-44d9-93af-e72b05012813',
owner: '<an-owner>',
id: 4448795656273,
name: 'Product 11'
}
],
upsellProducts: [{
userId: '89217803-5fff-44d9-93af-e72b05012813',
owner: '<an-owner>',
id: 4448796573777,
title: 'Product 13'
}],
name: 'ALL'
}
Using above schema, mutation, and payload, I got an error that saying subscriptionProducts field is not defined. To make sure that the schema is correct, I try to exclude subscriptionProducts and upsellProducts and it successfully added into AppSync. When I try to get it again from the AppSync, it shown me subscriptionProducts and upsellProducts properties which have null as a value. Do anyone of you know what is the correct mutation / payload that I should use based on the schema above?
PS: I am using aws-appsync as a client library