Strapi how to extra api in controller - node.js

I have completed a shopping cart and can pay to stripe, but I want to be able to know who bought it, so I want to add a feature that allows me to see the user in the strapi backend
I hope to know how to combine those that two functions.
I have tried their functions separately I will work for the different page
../controllers/order.js
const stripe = require('stripe')('xxxvxx');
const { parseMultipartData, sanitizeEntity } = require('strapi-utils');
module.exports = {
create: async ctx => {
const {
address,
amount,
product,
token,
} = ctx.request.body;
// Charge the customer
try {
// stripe charge
await stripe.customers
.create({
email: ctx.state.user.email,
source: token
})
.then((customer) => {
return stripe.charges.create({
// Transform cents to dollars.
amount: amount * 100,
currency: 'usd',
description: `Order ${new Date()} by ${ctx.state.user.id}`,
customer: customer.id
});
});
// Register the order in the database
try {
const order = await strapi.services.order.create({
user: ctx.state.user.id,
address,
amount,
product,
});
//email
if (order.id){
await strapi.plugins['email'].services.email.send({
to: ctx.state.user.email,
subject: 'Thank you for your purchase',
text: `
usd${order.amount} is charged from your credit card on Stripe.
usd${order.address} is charged from your credit card on Stripe.
`,
});
}
return order;
} catch (err) {
console.log(err)
}
} catch (err) {
console.log(err)
}
}
};
Want to combine it
async create(ctx) {
let entity;
if (ctx.is('multipart')) {
const { data, files } = parseMultipartData(ctx);
data.users_permissions_user = ctx.state.user.id;
entity = await strapi.services.about.create(data, { files });
} else {
ctx.request.body.users_permissions_user = ctx.state.user.id;
entity = await strapi.services.about.create(ctx.request.body);
}
return sanitizeEntity(entity, { model: strapi.models.about });

In the new strapi
user => users_permissions_user
the answer is // Register the order in the database
user: ctx.state.user.id,
try
users_permissions_user: ctx.state.user.id,

Related

Load confirmation URL in Embedded app itself in Shopify

I have an embedded app in shopify which is an paid app ,Once user approves the billing ,i want the app to show the confirmation url in the embedded app itself instead it loads externally.
getsubscriptionurl.js
export const getSubscriptionUrl = async (ctx, shop) => {
const { client } = ctx;
console.log(`process.env.HOST - ${process.env.HOST}`);
console.log(`shop - ${shop}`);
console.log(`${process.env.HOST}/?shop=${shop}`);
const confirmationUrl = await client
.mutate({
mutation: RECURRING_CREATE(),
variables: {
returnUrl: `www.abc.com`,
}
})
.then(response => response.data.appSubscriptionCreate.confirmationUrl);
console.log("me "+ confirmationUrl);
return ctx.redirect(confirmationUrl);
};
server.js
app.prepare().then(async () => {
const server = new Koa();
const router = new Router();
server.keys = [Shopify.Context.API_SECRET_KEY];
server.use(
createShopifyAuth({
async afterAuth(ctx) {
// Access token and shop available in ctx.state.shopify
const { shop, accessToken, scope } = ctx.state.shopify;
const host = ctx.query.host;
ACTIVE_SHOPIFY_SHOPS[shop] = {scope:scope,accessToken:accessToken};
const response = await Shopify.Webhooks.Registry.register({
shop,
accessToken,
path: "/webhooks",
topic: "APP_UNINSTALLED",
webhookHandler: async (topic, shop, body) =>
delete ACTIVE_SHOPIFY_SHOPS[shop],
});
if (!response.success) {
console.log(
`Failed to register APP_UNINSTALLED webhook: ${response.result}`
);
}
// Redirect to app with shop parameter upon auth
// ctx.redirect(`/?shop=${shop}&host=${host}`);
server.context.client = await handlers.createClient(shop, accessToken);
await handlers.getSubscriptionUrl(ctx, shop);
},
})
);
You can't basically show the confirmation URL in your app, Shopify won't trust app developers to take sensitive info like payment details, so must open the confirmation URL into a new tab, where the merchant is viewing a Shopify payment page(made by shopify) that contains the payment details to be entered and on confirm the page will redirect the merchant to the return URL as you specified before.
For testing purposes
you can send a test param within the query to allow you to test without entering any payment details
const CREATE_SUB_MUTATION_RECURRING_ONLY = gql`
mutation RecurringSubscription(
$returnUrl: URL!
$test: Boolean!
$planName: String!
$amount: Decimal!
) {
appSubscriptionCreate(
test: $test
name: $planName
returnUrl: $returnUrl
lineItems: [
{
plan: {
appRecurringPricingDetails: {
price: { amount: $amount, currencyCode: USD }
interval: EVERY_30_DAYS
}
}
}
]
) {
userErrors {
field
message
}
confirmationUrl
appSubscription {
id,
currentPeriodEnd
}
}
}
`;
Now to test just pass true to test
result = await graphQlClient?.mutate({
mutation: CREATE_SUB_MUTATION_RECURRING_ONLY,
variables: {
returnUrl,
test,
planName: PLANS_DATA[planName].planName,
amount: PLANS_DATA[planName].price,
},
});

How to return a response from a post request with cloud functions?

I am trying to implement a payment gateway with conekta but I cannot find a way to return the client code generated by conekta in the response of my post service.
app.post('/data/processPayment', async(req,res)=>{
try{
var customId = ""
const product_name =req.body.product_name
const unit_price =req.body.unit_price
const quantity =req.body.quantity
const username =req.body.username
const email =req.body.email
const phone = req.body.phone
const amount =req.body.amount
const carrier =req.body.carrier
const street1 =req.body.street1
const postal_code =req.body.postal_code
const description =req.body.description
const reference =req.body.reference
const type_payment =req.body.type_payment
const card_token = req.body.card_token
order = conekta.Order.create({
"line_items": [{
"name": product_name,
"unit_price": unit_price,
"quantity": quantity
}],
"shipping_lines": [{
"amount": amount,
"carrier": carrier
}], //optional - shipping_lines are only required for physical goods
"currency": "MXN",
"customer_info": {
'name': username,
'email': email,
'phone': phone
},
"shipping_contact":{
"address": {
"street1": street1,
"postal_code": postal_code,
"country": "MX"
}
}, //optional - shipping_contact is only required for physical goods
"metadata": { },
"charges":[{
"payment_method": {
'type': 'card',
'token_id': 'tok_test_visa_4242'
} //payment_methods - use the customer's default - a card
//to charge a card, different from the default,
//you can indicate the card's source_id as shown in the Retry Card Section
}]
}, function(err, res) {
if(err){
console.log(err);
return;
}
//How can I return this value in my final response to my post request?
customId = res.toObject().id
})
//I try to return the data but customId it does not work
return res.status(200).json({message:"OK ", folio:" "+customId});
}
catch(error)
{
console.log(error)
return res.status(500).send(error);
}
})
I am trying to implement a payment gateway with conekta but I cannot find a way to return the client code generated by conekta in the response of my post service.
The create() can be used with await instead of passing a callback. Try refactoring the code to:
app.post('/data/processPayment', async (req,res) => {
try {
// declare all constants
const newOrder = await conekta.Order.create({ ...params })
// read customer ID/email from newOrder
return res.status(200).json(newOrder)
} catch (e) {
return res.status(500).send(error)
}
}

stripe.confirmCardSetup(client_secret) not resulting in error

So I'm letting my user create a subscription with a free trial.
Node
const subscriptionWithTrial = async (req, res) => {
const {email, payment_method, user_id} = req.body;
const customer = await stripe.customers.create({
payment_method: payment_method,
email: email,
invoice_settings: {
default_payment_method: payment_method,
},
});
const subscription = await stripe.subscriptions.create({
customer: customer.id,
items: [{ plan: process.env.STRIPE_PRODUCT }],
trial_period_days: 14,
expand: ['latest_invoice.payment_intent', 'pending_setup_intent'],
});
res.json({subscription});
}
app.post('/subWithTrial', payment.subscriptionWithTrial);
React
const handleSubmitSubTrial = async (event) => {
if (!stripe || !elements) {
console.log("stripe.js hasn't loaded")
// Stripe.js has not yet loaded.
// Make sure to disable form submission until Stripe.js has loaded.
return;
}
const result = await stripe.createPaymentMethod({
type: 'card',
card: elements.getElement(CardElement),
billing_details: {
email: email,
},
});
stripe.createToken(elements.getElement(CardElement)).then(function(result) {
});
if (result.error) {
console.log(result.error.message);
} else {
try {
const res = await axios.post('/subWithTrial', {'payment_method': result.paymentMethod.id, 'email': email, 'user_id': auth0Context.user.sub});
const {pending_setup_intent, latest_invoice} = res.data.subscription;
if (pending_setup_intent) {
const {client_secret, status} = res.data.subscription.pending_setup_intent;
if (status === "requires_action") {
stripe.confirmCardSetup(client_secret).then(function(result) {
if (result.error) {
console.log('There was an issue!');
console.log(result.error);
} else {
// The setup has succeeded. Display a success message.
console.log('Success');
}
I'm using card number 4000008260003178 so I'm pretty sure it's supposed to require authentication (which works) then result into result.error for insufficient funds on line stripe.confirmCardSetup(client_secret).then(function(result) { if (result.error) {. But this doesn't happen. Instead it gives me console.log('Success');
This is expected. stripe.confirmCardSetup doesn't actually attempt to move money, it just sets the card up to do future off-session payments without needing 3DS.
Since you created your subscription with a 14 day trial, the actual first invoice payment won't happen till that's up. Meaning that in this case the test card is able to be set up correctly and won't see the payment failure till the trial period is up 14 days later.

Stripe "no such price"

I created a product with tiered pricing in my Stripe dashboard. I copied the price API IDs and put them in my app's frontend. When I run my code, the backend generates the error: No such price: 'PRICE_1HPYAGLJZYBC5S5KGBKT8UDY'. This price id matches one of the prices on my Stripe dashboard, but I never set the product so I'm wondering if that's the issue. Here is my client js:
function createSubscription({ customerId, paymentMethodId, priceId }) {
return (
fetch('/create-subscription', {
method: 'post',
headers: {
'Content-type': 'application/json',
},
body: JSON.stringify({
customerId: customerId,
paymentMethodId: paymentMethodId,
priceId: priceId,
}),
})
.then((response) => {
return response.json();
})
// If the card is declined, display an error to the user.
.then((result) => {
if (result.error) {
// The card had an error when trying to attach it to a customer
throw result;
}
return result;
})
// Normalize the result to contain the object returned
// by Stripe. Add the addional details we need.
.then((result) => {
console.log("RETURNING SUBSCRIPTION")
return {
// Use the Stripe 'object' property on the
// returned result to understand what object is returned.
subscription: result,
paymentMethodId: paymentMethodId,
priceId: priceId,
};
})
);
}
And here is my backend code:
app.post('/create-subscription', async function(req, res) {
console.log(req.body);
User.findOne({"_id": req.session.auth_user._id}, async function(err, user) {
if (user.stripe_id) {
console.log("RETRIEVING CUSTOMER");
var customer = await stripe.customers.retrieve(user.stripe_id);
if (user.stripe_subscription) {
console.log("RETRIEVING SUBSCRIPTION");
var subscription = await stripe.subscriptions.retrieve(user.stripe_subscription);
update_customer(customer, subscription);
}
else {
console.log("CREATING SUBSCRIPTION");
var subscription = await stripe.subscriptions.create({
customer: customer.id,
items: [{
price: req.body.priceId,
}]
});
user.stripe_subscription = subscription.id;
user.save(function(err) {
update_customer(customer, subscription);
})
}
}
else {
console.log("CREATING CUSTOMER");
var customer = await stripe.customers.create({
email: req.body.email,
});
user.stripe_id = customer.id;
user.save( async function(err, user) {
if (user.stripe_subscription) {
console.log("RETRIEVING SUBSCRIPTION");
var subscription = await stripe.subscriptions.retrieve(user.stripe_subscription);
update_customer(customer, subscription);
}
else {
console.log("CREATING SUBSCRIPTION");
var subscription = await stripe.subscriptions.create({
customer: customer.id,
items: [{
price: req.body.priceId,
}]
});
user.stripe_subscription = subscription.id;
user.save(function(err) {
update_customer(customer, subscription);
});
}
});
}
});
async function update_customer(customer, subscription) {
const paymentMethod = await stripe.paymentMethods.attach(
req.body.paymentMethodId,
{customer: customer.id}
);
console.log(subscription);
res.send(subscription);
}
});
Check the price ID, it looks like something in your frontend converts all the string to uppercase. Usually price id start in lowercase ('price....') and then the string is a mix between numbers and lowercase and uppercase characters.
Incase anyone faces this issue in the future. I had the same issue, but mine was caused by the stripe secret being wrong.
It's wise to:
Double check the price,
Trim the string,
Check your config keys all through
Hope this helps someone 🚀
In my case I was following the docs and
the docs had it like this: Price = "{{price_1234}}"
So I changed it to this: Price = "price_1234" and it worked.
In my case, the API keys were not correct.
If like me, you just followed the tutorial from the docs, the API keys from the code snippets that are proposed are not correct.
You have to setup the ones from the dashboard page (for the test environment: https://dashboard.stripe.com/test/dashboard)

Stripe: How do I add and store additional form elements with javascript?

I am trying store data from additional inputs in the form, not just the credit card information.
Here is what the form looks like:
form image
The payment works and the booking gets stored in the database. I am struggling to figure out how to also get the additional form inputs "a link to your project" and "additional info" stored in the DB as well.
Here is what the code likes on the server:
router.route('/pay')
.post(async (req, res, next) => {
let artist = req.session.artist;
let price = req.session.price;
let user = req.session.user;
let email = req.session.userEmail;
price *= 100;
const {
paymentMethodId,
paymentIntentId,
currency,
useStripeSdk
} = req.body;
try {
let intent;
if (paymentMethodId) {
// Create new PaymentIntent with a PaymentMethod ID from the client.
intent = await stripe.paymentIntents.create({
amount: price,
currency: currency,
receipt_email: email,
payment_method: paymentMethodId,
confirmation_method: 'manual',
confirm: true,
// If a mobile client passes `useStripeSdk`, set `use_stripe_sdk=true`
// to take advantage of new authentication features in mobile SDKs
use_stripe_sdk: useStripeSdk
});
// After create, if the PaymentIntent's status is succeeded, fulfill the order.
} else if (paymentIntentId) {
// Confirm the PaymentIntent to finalize payment after handling a required action
// on the client.
intent = await stripe.paymentIntents.confirm(paymentIntentId);
// After confirm, if the PaymentIntent's status is succeeded, fulfill the order.
}
let newBooking = await Booking.create({
artist,
user,
price,
projectLink: req.body.projectLink,
additionalInfo: req.body.additionalInfo
});
console.log(newBooking)
let data = {
intent: generateResponse(intent)
};
res.send(data);
} catch (e) {
// Handle "hard declines" e.g. insufficient funds, expired card, etc
// See https://stripe.com/docs/declines/codes for more
res.send({
error: e.message
});
}
});
And here is what it looks like on the client:
var stripe = Stripe('pk_test_ycz...');
// A reference to Stripe.js
var stripe;
var orderData = {
items: [{
id: "photo-subscription"
}],
currency: "usd"
};
// Disable the button until we have Stripe set up on the page
document.querySelector(".disable").disabled = true;
fetch("/stripe-key")
.then(function (result) {
return result.json();
})
.then(function (data) {
return setupElements(data);
})
.then(function ({
stripe,
card,
clientSecret
}) {
document.querySelector(".disable").disabled = false;
var form = document.getElementById("payment-form");
form.addEventListener("submit", function (event) {
event.preventDefault();
const projectLink = document.getElementById('projectLink').value;
const additionalInfo = document.getElementById('additionalInfo').value;
pay(stripe, card, clientSecret, projectLink, additionalInfo);
console.log(projectLink, additionalInfo)
});
});
var setupElements = function (data) {
stripe = Stripe(data.publishableKey);
/* ------- Set up Stripe Elements to use in checkout form ------- */
var elements = stripe.elements();
var style = {
base: {
color: "#32325d",
fontFamily: '"Helvetica Neue", Helvetica, sans-serif',
fontSmoothing: "antialiased",
fontSize: "16px",
"::placeholder": {
color: "#aab7c4"
}
},
invalid: {
color: "#fa755a",
iconColor: "#fa755a"
}
};
var card = elements.create("card", {
style: style
});
card.mount("#card-element");
return {
stripe: stripe,
card: card,
clientSecret: data.clientSecret
};
};
var handleAction = function (clientSecret, projectLink, additionalInfo) {
stripe.handleCardAction(clientSecret).then(function (data) {
if (data.error) {
showError("Your card was not authenticated, please try again");
} else if (data.paymentIntent.status === "requires_confirmation") {
fetch("/pay", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
paymentIntentId: data.paymentIntent.id
})
})
.then(function (result) {
return result.json();
})
.then(function (json) {
if (json.error) {
showError(json.error);
} else {
orderComplete(clientSecret);
}
});
}
});
};
/*
* Collect card details and pay for the order
*/
var pay = function (stripe, card) {
changeLoadingState(true);
// Collects card details and creates a PaymentMethod
stripe
.createPaymentMethod("card", card)
.then(function (result) {
if (result.error) {
showError(result.error.message);
} else {
orderData.paymentMethodId = result.paymentMethod.id;
return fetch("/pay", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(orderData)
});
}
})
.then(function (result) {
return result.json();
})
.then(function (response) {
if (response.intent.error) {
showError(response.intent.error);
} else if (response.intent.requiresAction) {
// Request authentication
handleAction(response.intent.clientSecret);
} else {
orderComplete(response.intent.clientSecret);
}
});
};
On the server side, I have used req.body for both inputs on Booking.create(...) but I'm not sure how to get that from the client using Stripe's code. On the client side, I tried adding the form data to the fetch API call on /pay but couldn't figure out how to get it to work. If anyone has any ideas, that would be awesome. Thanks
You're not passing the additional info you want as part of the body of fetch("/pay"), which is how it would get to the server side. You should add them as arguments to your client side pay() function, and then pass them in the body of that fetch request.

Resources