Why is my PaymentMethod ID not detected when I run my AWS stripe application via Docker? - visual-studio-code

I am learning web development and I am currently working on creating a lambda test application for stripe. The paymentMethod id from the front-end is not being detected by my lambda function when I run it locally by calling sam local start-api. I am doing my development on VS Code.
I followed the instructions on this page to create and run my application. My directory structure looks like this:
hello_world/app.py has my Lambda function.
The code for invoking the lambda end-point in script.jslooks like this:
var stripe = Stripe('pk_test_DIGITS');
var elements = stripe.elements();
form.addEventListener('submit', function(event) {
// We don't want to let default form submission happen here,
// which would refresh the page.
event.preventDefault();
stripe.createPaymentMethod({
type: 'card',
card: cardElement,
billing_details: {
// Include any additional collected billing details.
name: 'Jenny Rosen',
},
}).then(stripePaymentMethodHandler);
});
function stripePaymentMethodHandler(result) {
if (result.error) {
// Show error in payment form
} else {
// Otherwise send paymentMethod.id to your server (see Step 4)
fetch('http://127.0.0.1:3000/payment', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
payment_method_id: result.paymentMethod.id,
})
}).then(function(result) {
// Handle server response (see Step 4)
result.json().then(function(json) {
handleServerResponse(json);
})
});
}
}
I ran the application on the browser by doing this:
When I click on Pay from my browser I can see the response in the logs on my dashboard:
The following code is for my lambda function app.py:
import json
import stripe
import requests
import logging
stripe.api_key= "sk_test_DIGITS"
def process_payment(event, context):
try:
print("START PRINTING")
print(event)
print("END PRINTING")
intent = stripe.PaymentIntent.create(
payment_method = 'event["body"]["payment_method_id"]',
amount = 1555,
currency = 'usd',
confirmation_method = 'automatic',
confirm = True,
payment_method_types=["card"]
)
return {
"statusCode": 200,
"body": json.dumps({
'clientSecret': intent['client_secret'],
# "location": ip.text.replace("\n", "")
}),
}
except Exception as e:
return {
"statusCode": 400,
"body": json.dumps({
"message": str(e),
# "location": ip.text.replace("\n", "")
}),
}
My template.yaml is as follows:
Globals:
Function:
Timeout: 30
Resources:
StripePaymentProcessor:
Type: AWS::Serverless::Function
Properties:
CodeUri: hello_world/
Handler: app.process_payment
Runtime: python3.6
Events:
Payment:
Type: Api
Properties:
Path: /payment
Method: post
Outputs:
HelloWorldApi:
Description: "API Gateway endpoint URL for Prod stage for Payment function"
Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/payment/"
HelloWorldFunction:
Description: "Payment Lambda Function ARN"
Value: !GetAtt StripePaymentProcessor.Arn
HelloWorldFunctionIamRole:
Description: "Implicit IAM Role created for Payment function"
Value: !GetAtt StripePaymentProcessorRole.Arn
While keeping the browser window open, I ran the sam build command and it worked properly. After that I ran the sam local invoke command and it produced the following output:
I do not understand why event is empty. Should it not show the JSON data that got produced when I hit the pay button?
To do some trouble-shooting, I ran sam local start-api, invoked the POST method on Postman by pasting the JSON body from my Stripe logs:
What I did on Postman makes no sense to me and the snippet above raised another question for me. I do not understand why I see "message": "string indices must be integers" as a response on Postman.
EDIT:
After following wcw's suggestion I edited my fetch code to look like this:
I did not not see any written matter on the console by changing my code in this way.
I am keeping the browser open via the command prompt and I ran sam local start-api via the VS code console to keep http://127.0.0.1:3000/payment open. When I clicked on the pay button, I got the following response:
So the image above seems to indicate that the lambda function is not detecting the paymentmethod body.

Related

How to catch Next-Auth Email Provider's sign in errors in the client with redirect property set to 'false'?

I added Next-Auth's Email provider to my app, and having issues with catching signIn errors in the client. According to documentation as well as this answer, when using signIn with 'redirect: false' it will return a Promise, that resolves to the following:
{
error: string | undefined;
status: number;
ok: boolean;
url: string | null;
}
In case of errors, however, the 'error' property of the response object has only 'EmailSignin' value, and contains no other information about the kind of error. Instead, more detailed errors are printed in the terminal.
I have the following basic setup:
[...nextauth].js
EmailProvider({
name: "Email",
server: {
host: "smtp.gmail.com",
port: "587",
auth: {
user: "myusername",
pass: "mypassword",
},
},
from: "My App",
}),
And the code of my custom sign in form (modal window):
const handleSignInClick = async () => {
const { email } = formData;
const response = await signIn("email", {
redirect: false,
email,
});
...
...
...
};
Is there any way to catch the errors that are printing in the console, and send them to client instead?
Ok, after playing a little bit with the code + additional reading of the documentation I came up with a solution.
Basically, when you add normalizeIdentifier method to EmailProvider, it overrides the default normalization mechanism. In the method I return 'identifier' without any changes. This stops Next-Auth's logger from throwing 'invalid email' errors to the console. Here is the code:
'normalizeIdentifier' method
EmailProvider({
name: "Email",
server: {
host: "smtp.gmail.com",
port: "587",
auth: {
user: "myusername",
pass: "mypassword",
},
},
from: "My App",
normalizeIdentifier(identifier) {
// return indentifier as is, to avoid next auth logger to log failure for invalid email
return identifier;
},
})
In addition to that, I added 'signIn' callback. Which as per documentation runs 2 times. First when verification request is sent, and second after a user has clicked on a sign-in link. In the first run, you can check 'verificationRequest' which will be 'true'. And here you can do validation of the email, as well and throw an error (which will be sent to client).
'signIn' callback
callbacks: {
async signIn({ user: { email }, email: { verificationRequest } }) {
if (verificationRequest) {
try {
// validate email here
validateEmail(email);
} catch {
//thrown error will be sent to client
throw "Email is invalid";
}
}
},
},
Here is response for invalid request:
{
"error": "Email is invalid",
"status": 200,
"ok": true,
"url": null
}
NOTE: There is only one case in which this solution will not work, and it is when email is not provided at all (normalizeIdentifier and signIn will not be triggered). So default 'EmailSignin' error is sent to client. IMO this is still OK, because logger does not pollute the console, and you know that if it's not a custom error, then there was no email provided.

Can't login by Goggle accounts connect or by Google auth API in Cypress 10x

Current behavior
I've tried to connect to Google account when my tested application redirects to Google accounts connect for let the end-user send emails by the application but I'm not able to do it not by Google Auth API according to your guidelines:
https://docs.cypress.io/guides/end-to-end-testing/google-authentication#Custom-Command-for-Google-Authentication
and not by cy.origin() from the UI.
In the first attempt by the API it's ignore of these authentication and popup the dialog to connect by google account as usually even all the credentials and token are valid and return 200 ok.
In the second attempt by cy.origin() it's keep to load the page after the redirect and always reach to timeout and yell about to increase the timeout even the page seems like it was fully loaded after a few seconds.
I've tried to increase the timeout to 90 seconds and use wait() before and after the redirect and look for some hidden iframes and tried every versa of google domain but nothing help.
it always return errors over there.
all the examples are below.
This is the error when trying to use cy.origin()::
Timed out after waiting 30000ms for your remote page to load on origin(s):
- https://google.com
A cross-origin request for https://accounts.google.com/o/oauth2/v2/auth?access_type=offline&scope=https%3A%2F%2Fmail.google.com&include_granted_scopes=true&state=%7B%22redirectUri%22%3A%22https%3A%2F%2Fmyappurl.com%2Fapp%2Fpipeline%2F9some-token-here-b96b599154ac%3Ftab%3Doverview%22%2C%22clientToken%22%3A%mytokenishere-1234567890%22%7D&prompt=consent&response_type=code&client_id=1234567890-aehhht36f7a01d38bmsvvpjrh915i86v.apps.googleusercontent.com&redirect_uri=https%3A%2F%2Fmyredreictedappurl.com%2FusersManagerSrvGoogleLogin was detected.
A command that triggers cross-origin navigation must be immediately followed by a cy.origin() command:
cy.origin('https://google.com', () => {
<commands targeting https://accounts.google.com go here>
})
If the cross-origin request was an intermediary state, you can try increasing the pageLoadTimeout value in Users/myname/repos/myreponame/cypress.config.ts to wait longer.
Browsers will not fire the load event until all stylesheets and scripts are done downloading.
When this load event occurs, Cypress will continue running commands.[Learn more](https://on.cypress.io/origin)
Desired behavior
No response
Test code to reproduce
commands.ts
Cypress.Commands.add('loginByGoogleApi', () => {
cy.log('Logging in to Google')
cy.request({
method: 'POST',
url: 'https://www.googleapis.com/oauth2/v4/token',
body: {
grant_type: 'refresh_token',
client_id: Cypress.env('googleClientId'),
client_secret: Cypress.env('googleClientSecret'),
refresh_token: Cypress.env('googleRefreshToken'),
},
}).then(({ body }) => {
const { access_token, id_token } = body
cy.request({
method: 'GET',
url: 'https://www.googleapis.com/oauth2/v3/userinfo',
headers: { Authorization: `Bearer ${access_token}` },
}).then(({ body }) => {
cy.log(body)
const userItem = {
token: id_token,
user: {
googleId: body.sub,
email: body.email,
givenName: body.given_name,
familyName: body.family_name,
imageUrl: body.picture,
},
}
window.localStorage.setItem('googleCypress', JSON.stringify(userItem))
cy.visit('/')
})
})
})
test-file.cy.ts
it.only('Send email to a user - is shown in the activity', () => {
cy.loginByGoogleApi();
cy.get(loc.sideNavBar.buyersPipeline).should('be.visible').click();
cy.get(loc.pipelineBuyer.nameColumn)
.eq(4)
.should('be.visible')
.click({ force: true });
cy.get(loc.buyerDetails.basicCard).should('be.visible');
cy.get(loc.buyerDetails.timelineSendEmailIcon)
.should('be.visible')
.click();
cy.get('div[role="dialog"]').find('button.MuiButton-root').should('be.visible').click();
})
})
By cy.origin() by the UI:
test-file.cy.ts
it.only('Send email to a user - is shown in the activity', () => {
// cy.loginByGoogleApi();
cy.get(loc.sideNavBar.buyersPipeline).should('be.visible').click();
cy.get(loc.pipelineBuyer.nameColumn)
.eq(4)
.should('be.visible')
.click({ force: true });
cy.get(loc.buyerDetails.basicCard).should('be.visible');
cy.get(loc.buyerDetails.timelineSendEmailIcon)
.should('be.visible')
.click();
cy.get('div[role="dialog"]').find('button.MuiButton-root').should('be.visible').click();
cy.wait(5000);
cy.origin('https://accounts.google.com', () => {
cy.wait(5000);
expect(window.origin).contains('google.com')
cy.get('input[type="email"]', {timeout: 60000}).should('be.visible', {timeout: 60000}).type('111');
})
});
````
### Cypress Version
10.7.0
### Node version
v14.19.1
### Operating System
macOS Montery 12.3.1

Capturing Paypal payment always fails with PAYER_ACTION_REQUIRED in production

Several weeks ago, we started seeing failed payments with our old Paypal Express integration without making any changes to it. They did not fail 100% but maybe 80%-90%. It seemed to fail arbitrarily.
We followed the upgrade path for the new Checkout integration, but kept seeing the same errors.
Finally we went all in on the new Standard (not Advanced!) Checkout integration using the Orders v2 API, replacing the old Payments V1 API. But that did not change anything either.
Side note: All of the integrations (old, updated, new) work totally fine in Sandbox.
But in production, almost every capture request fails with a PAYER_ACTION_REQUIRED error.
Here are the the steps:
User clicks on the checkout with paypal button.
The button's createOrder() method calls our server and we create an order with a POST to https://api.paypal.com/v2/checkout/orders, with the following response:
{
id: '1KC867444K565180X',
status: 'CREATED',
links: [
{
href: 'https://api.paypal.com/v2/checkout/orders/1KC867444K565180X',
rel: 'self',
method: 'GET'
},
{
href: 'https://www.paypal.com/checkoutnow?token=1KC867444K565180X',
rel: 'approve',
method: 'GET'
},
{
href: 'https://api.paypal.com/v2/checkout/orders/1KC867444K565180X',
rel: 'update',
method: 'PATCH'
},
{
href: 'https://api.paypal.com/v2/checkout/orders/1KC867444K565180X/capture',
rel: 'capture',
method: 'POST'
}
]
}
We do not redirect to the approve URL (since the button should handle it on the frontend, right?). So we just hand the ID back to the button's createOrder() method.
The onApprove() callback then calls our server again with the orderId and the paymentId. We do not capture the payment yet. Instead, we fetch the details to get the shipping address. After calculating the shipping costs, we PATCH the order. Then we fetch details again and verify that all changes have been applied correctly - which ist the case.
Side note: When fetching details after creating the order (so after it went through the buttons onApprove() callback, and right before capturing), there is no more approve link listed:
{
id: '1KC867444K565180X',
intent: 'CAPTURE',
status: 'APPROVED',
purchase_units: [
{
//...
}
],
payer: {
//...
},
create_time: '2022-07-14T14:41:16Z',
links: [
{
href: 'https://api.paypal.com/v2/checkout/orders/1KC867444K565180X',
rel: 'self',
method: 'GET'
},
{
href: 'https://api.paypal.com/v2/checkout/orders/1KC867444K565180X',
rel: 'update',
method: 'PATCH'
},
{
href: 'https://api.paypal.com/v2/checkout/orders/1KC867444K565180X/capture',
rel: 'capture',
method: 'POST'
}
]
}
Up until now, sandbox and production behave excatly the same. But when we now try to capture the payment, sandbox works fine, but production returns this:
{
name: 'UNPROCESSABLE_ENTITY',
details: [
{
issue: 'PAYER_ACTION_REQUIRED',
description: 'Payer needs to perform the following action before proceeding with payment.'
}
],
message: 'The requested action could not be performed, semantically incorrect, or failed business validation.',
debug_id: '160e81e8cc53f',
links: [
{
href: 'https://developer.paypal.com/docs/api/orders/v2/#error-PAYER_ACTION_REQUIRED',
rel: 'information_link',
method: 'GET'
}
]
}
We completely ran out of ideas what to do with this message. The docs suggest that "The order requires an action from the payer (e.g. 3DS authentication). Redirect the payer to the "rel":"payer-action" HATEOAS link returned as part of the response prior to authorizing or capturing the order." - but there is no such link. Plus, the order status is already APPROVED.
Any help would be highly appreciated.
It's probably related to having patched the order before capture, requiring re-approval.
Adapt the error handling code in this demo to also call actions.restart() when the issue is PAYER_ACTION_REQUIRED .
Essentially,
if (errorDetail && ['INSTRUMENT_DECLINED', 'PAYER_ACTION_REQUIRED'].includes(errorDetail.issue) ) {
Alternatively, instead of the button JS you can use the rel:approve href from the original order creation and redirect to that.
this error is due to the EBA PSD2.0 regulation. any order is paid through credit card issued from EMEA region, and the capture amount is higher than the create order amount, it will be declined with the error code. here's the PayPal change about the over capture:
https://www.paypal.com/ae/smarthelp/article/FAQ4645

Implementing Handover protocol for facebook chatbot throws error

My primary receiver is my dialogflow chatbot and my second one is the page inbox. I want to switch from the chatbot to the inbox by implementing:
request({
uri: 'https://graph.facebook.com/v3.2/me/pass_thread_control',
qs: {'access_token': 'pageAccessToken'},
method: 'POST',
json: {
recipient: {
id: 'userIdFromRequest'
},
target_app_id: 'pageInboxAppId'
},
}, (error: any, response: any, body: any) => {
console.log('BODY PASS THREAD CONTROL: ', body);
});
But I'm getting this error:
(#10) Only available to Primary and Secondary Receivers
When I try to get the secondary receivers:
request({
uri: 'https://graph.facebook.com/v3.2/me/secondary_receivers?fields=id,name&access_token=<page-access-token>',
qs: {'access_token': <page-access-token>},
method: 'GET',
}, (error: any, response: any, body: any) => {
console.log('BODY SECONDARY RECEIVERS: ', body);
});
I will get this error:
(#10) Only Main Receiver can call this API
But my chatbot IS the main receiver. I set in in page settings --> messenger platform.
I found out that this used to be a facebook bug, but it should have been fixed now.
Does someone has an idea what I'm doing wrong?
UPDATE
The app_roles parameter is missing in the web hook request. May it could have something to do with it?
Another mistake could be the userId. I took the senderId. The receiverId encounters an authorization error with errorCode 1 and the message "An unknown error occurred". I'm not sure which one is correct.<
UPDATE 2
The receiverId seems to be correct. The second request works with it. But still not the first: 500 internal server error...
Suddenly, it works now. Maybe they changed anything in the backend.

PayPal Express checkout - does not seem to run successfully

So, I am trying to figure this out but cannot seem to get this right.
I am trying to integrate paypal express onto a website. I have the sandbox environment with the facilitator and buyer accounts.
I paste in the sample code and change the client values and transaction details etc... like so:
paypal.Button.render({
env: 'sandbox', // Optional: specify 'sandbox' environment
client: {
sandbox: 'My Client ID here'
},
payment: function () {
var env = this.props.env;
var client = this.props.client;
return paypal.rest.payment.create(env, client, {
intent: "sale",
payer: { payment_method: "paypal" },
transactions: [
{
amount: { total: '14.00', currency: 'GBP' },
description: "This is a payment description"
},
],
redirect_urls: {
return_url: "http://somesite.com/success",
cancel_url: "http://somesite.com/cancel"
}
});
},
commit: true, // Optional: show a 'Pay Now' button in the checkout flow
onAuthorize: function (data, actions) {
// Optional: display a confirmation page here
alert('confirmation here');
return actions.payment.execute().then(function () {
// Show a success page to the buyer
alert('success here');
});
}
}, '#paypal-button');
The issue I am facing is that when you hit the paypal button - in the popup I do not see the amount.
Then when continuing, I get an error from javascript like so:
JavaScript runtime error: Error: Payment Api response error:
{
"name": "INTERNAL_SERVICE_ERROR",
"message": "An internal service error has occurred",
"information_link": "https://developer.paypal.com/docs/api/#INTERNAL_SERVICE_ERROR",
"debug_id": "a9ceebeb96bab"
}
Error: Payment Api response error:
{
"name": "INTERNAL_SERVICE_ERROR",
"message": "An internal service error has occurred",
"information_link": "https://developer.paypal.com/docs/api/#INTERNAL_SERVICE_ERROR",
"debug_id": "a9ceebeb96bab"
}
at Anonymous function (https://www.paypalobjects.com/api/checkout.js:8325:17)
at _loop2 (https://www.paypalobjects.com/api/checkout.js:1509:25)
at SyncPromise.prototype.dispatch (https://www.paypalobjects.com/api/checkout.js:1542:17)
at SyncPromise.prototype.resolve (https://www.paypalobjects.com/api/checkout.js:1480:13)
at Anonymous function (https://www.paypalobjects.com/api/checkout.js:1533:25)
at _loop2 (https://www.paypalobjects.com/api/checkout.js:1509:25)
at SyncPromise.prototype.dispatch (https://www.paypalobjects.com/api/checkout.js:1542:17)
at SyncPromise.prototype.resolve (https://www.paypalobjects.com/api/checkout.js:1480:13)
at Anonymous function (https://www.paypalobjects.com/api/checkout.js:1533:25)
at _loop2 (https://www.paypalobjects.com/api/checkout.js:1509:25)
So how the heck do I know what the error is?
Second question: Hopefully when this is all working with your help, how can I get back the response and determine if its a success or failure of payment along with detailed information to log it on my website and display to the user? What about custom variables like paymentID generated from my site? Can I get that information back?
Thank you
You're missing some important information for the API.
Things like AMOUNT{}, and others.
https://developer.paypal.com/docs/integration/direct/create-process-order/
I have a problem with this as well, I can get to the actions.payment.execute() and I get an error, _loop2 etc etc.