Apple Pay - "Payment Not Completed" - Using Stripe - ios

I am using stripes "Payment Request Button" to implement Apple Pay for my website. On the stripe side of things all is well. The token is passed through correcty as I verified within Stripe logs.
https://stripe.com/docs/stripe-js/elements/payment-request-button
However, I get an error message: "Payment Not Completed" from Apple Pay every time I try to complete a test payment.
This has me stuck and I'm not sure how to debug or fix. Any ideas?
I get an undefined token
This is the error:
My set up:
FRONT END:
<script src="https://js.stripe.com/v3/"></script>
<div id="payment-request-button">
<!-- A Stripe Element will be inserted here. -->
</div>
<script>
var stripe = Stripe('pk_test_xxxxx');
var paymentRequest = stripe.paymentRequest({
country: 'US',
currency: 'usd',
total: {
label: 'JobQuiz',
amount: 999,
},
requestPayerName: true,
requestPayerEmail: false,
});
var elements = stripe.elements();
var prButton = elements.create('paymentRequestButton', {
paymentRequest: paymentRequest,
});
// Check the availability of the Payment Request API first.
paymentRequest.canMakePayment().then(function(result) {
if (result) {
prButton.mount('#payment-request-button');
} else {
document.getElementById('payment-request-button').style.display = 'none';
}
});
paymentRequest.on('token', function(ev) {
// Send the token to your server to charge it!
fetch('/apple-pay', {
method: 'POST',
body: JSON.stringify({token: ev.token.id}),
headers: {'content-type': 'application/json'},
})
.then(function(response) {
if (response.ok) {
// Report to the browser that the payment was successful, prompting
// it to close the browser payment interface.
ev.complete('success');
} else {
// Report to the browser that the payment failed, prompting it to
// re-show the payment interface, or show an error message and close
// the payment interface.
ev.complete('fail');
}
});
});
</script>
Server side code in app.js
app.post('/apple-pay', function(req, res, next) {
// Set your secret key: remember to change this to your live secret key in production
// See your keys here: https://dashboard.stripe.com/account/apikeys
var stripe = require("stripe")("sk_test_xxxxxx");
// Token is created using Checkout or Elements!
// Get the payment token ID submitted by the form:
const token = req.body.token; // Using Express
const charge = stripe.charges.create({
amount: 999,
currency: 'usd',
description: 'Example charge',
source: token,
}, function(err, charge){
if (err){
} else {
}
});
});

Finally solved this. It ended up being an issue with my bodyParser set up. This explains why token was being passed though empty. I had neglected to include app.use(bodyParser.json()); below...
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());

Related

Expo, React Native, Stripe: Setting up future payment methods not working

I’m in desperate need for help.
So I have a side project for an iOS app using Expo / React Native. And I'm having issues with setting up future payment methods using Stripe & Expo’s stripe library.
Our back-ender set up a graphql back-end, and provides me with all the variables I need. I’m trying to set up future payments to charge clients later, but I’m having trouble having with the paymentIntentSheet not showing up after creating an intent and fetching the clientSecret, ephemeralKey and customerId from our back-end. Now i don’t know where the issue is.. Is it because of me using the wrong versions? Maybe incorrect installation? Are the variables I’m using right..?
I used the following documentation page(s) as a guide:
https://stripe.com/docs/payments/save-and-reuse?platform=react-native
https://github.com/stripe/stripe-react-native#expo
These are the version numbers of the libraries I’m using, relevant to this topic/issue:
"expo": "~41.0.1",
"react": "16.13.1",
"react-dom": "16.13.1",
"react-native": "https://github.com/expo/react-native/archive/sdk-41.0.0.tar.gz",
"#stripe/stripe-react-native": "0.1.1"
These are the steps I took:
Install stripe-react-native, and add it to my app.json as a plugin:
"plugins": [
[
"#stripe/stripe-react-native",
{
"merchantIdentifier": "",
"enableGooglePay": false
}
]
],
On global level, I import the StripeProvider component and pass down the given publishable key:
pk_live_51[.....]
On global level it’ll look like this:
<StripeProvider
publishableKey="pk_live_51[...]"
>
<AuthProvider>
<ApolloProvider client={client}>
<InnerApp />
</ApolloProvider>
</AuthProvider>
</StripeProvider>
Then according to the stripe docs, at the component where I'll be setting up future payments, I am supposed to fetch the setupIntent, ephemeralKey, and the customer from the back-end. In this case, it's done in the useEffect of my component. I was provided with a graphql mutation to obtain these values:
mutation (
$createUserPaymentMethodSetupIntentInput: CreateUserPaymentMethodSetupIntentInput!
) {
createUserPaymentMethodSetupIntent(
input: $createUserPaymentMethodSetupIntentInput
) {
setupIntentId
clientSecret
customerId
ephemeralKeySecret
}
}
I then call the function that will eventually provide me with all the necessary variables:
createIntent({
variables: {
createUserPaymentMethodSetupIntentInput: {
userUid: userUid,
},
},
})
.then((res) => {
const clientSecret =
res.data.createUserPaymentMethodSetupIntent.clientSecret
const setupIntentId =
res.data.createUserPaymentMethodSetupIntent.setupIntentId
const ephemeralKeySecret =
res.data.createUserPaymentMethodSetupIntent.ephemeralKeySecret
const customerId =
res.data.createUserPaymentMethodSetupIntent.customerId
// IGNORE THIS FOR NOW
initializePaymentSheet(
clientSecret,
setupIntentId,
ephemeralKeySecret,
customerId
)
})
.catch((err) => console.log({ graphqlError: err }))
The function gives me the following response:
Object {
"data": Object {
"createUserPaymentMethodSetupIntent": Object {
"__typename": "CreatedUserPaymentMethodSetupIntent",
"clientSecret": "seti_1K[....]",
"customerId": "cus_[...]",
"ephemeralKeySecret": "ek_live_[...]",
"setupIntentId": "seti_[...]",
},
},
According to the docs, I should use the setupIntent, ephemeralKey, and customer values as variables in one of their given functions/hooks called “initPaymentSheet” which should initialize the paymentsheet on their end.
These functions are imported like this:
const { initPaymentSheet, presentPaymentSheet } = useStripe();
In step 3, you see that I call a function that then calls the initPaymentSheet after successfully fetching the values from the server.
initializePaymentSheet(
clientSecret,
setupIntentId,
ephemeralKeySecret,
customerId
)
The initializePaymentSheet function looks like this:
const initializePaymentSheet = (
clientSecret,
setupIntentId,
ephemeralKeySecret,
customerId
) => {
initPaymentSheet({
customerId: customerId,
customerEphemeralKeySecret: ephemeralKeySecret,
setupIntentClientSecret: setupIntentId,
})
.then((res) => {
console.log(res)
setDisabledButton(false)
})
.catch((err) => console.log("error.."))
}
As you can see, I call the initPaymentSheet hook there, exactly like shown on the docs, and pass in the values i received from the back-end. However, after doing this i get the following error in the console:
Object {
"error": Object {
"code": "Failed",
"message": "You must provide the paymentIntentClientSecret",
},
}
This didn’t seem like a huge error, so I went ahead and changed the initPaymentSheet parameters by adding the paymentIntentClientSecret field and passed in the clientSecret value which wasn’t previously used:
initPaymentSheet({
customerId: customerId,
customerEphemeralKeySecret: ephemeralKeySecret,
setupIntentClientSecret: setupIntentId,
paymentIntentClientSecret: clientSecret
})
.then((res) => {
console.log(res)
setDisabledButton(false)
})
.catch((err) => console.log("little error.."))
After calling the function and seeing the error disappear, and the console.log shown above logs the following in the console:
Object {
"paymentOption": null,
}
I didn’t think too much of this, and thought it says null just because I have no previously set paymentOptions. I was just happy there were no more errors.
In the .then chain, you see that i enable a button that basically allows a user to call a function that would present a payment sheet where users can submit their paymentMethod. This button is disabled, because I think you should initialize the paymentSheet first before enabling it?
<WideButton
disabled={disabledButton}
text="Add New Payment Method"
clicked={openPaymentSheet}
/>
Anyways, now that the button is finally enabled, the user can click on it and it'll call the following function:
const openPaymentSheet = async () => {
setDisabledButton(true)
const { error, paymentOption } = await presentPaymentSheet()
if (error) {
console.log(error)
setDisabledButton(false)
Alert.alert(`Error code: ${error.code}`, error.message)
}
if (paymentOption) {
setDisabledButton(false)
Alert.alert(
"Success",
"Your payment method is successfully set up for future payments!"
)
console.log(paymentOption)
}
}
Now to quote the stripe docs:
When your customer taps the Set up button, call presentPaymentSheet() to open the sheet. After the customer completes setting up their payment method for future use, the sheet is dismissed and the promise resolves with an optional StripeError.
So, that's exactly what I did: Call the presentPaymentSheet, but then i get the following error:
Object {
"code": "Failed",
"message": "There was an unexpected error -- try again in a few seconds",
}
Now this is where I’m stuck, because it doesn’t provide me with any more information than given above. I’ve tried looking everywhere, and some resources tell me that I should update my stripe, some say i should add stripe to my plugins in app.json. I’ve done all of that and I can’t still figure it out.
Here is a video showing you the behavior in action:
https://user-images.githubusercontent.com/29804130/146274443-82c581ba-8913-4c87-ad2e-5b8719680fed.mov
Here is the code of the entire component:
// steps
// 1. call graphql query to set up intent, retrieve the clientsecret and setupintentid
// 2. call stripes initPaymentSheet's function and pass in useruid, clientsecret and setupintentid
// 3. when initpaymentsheet is ready, enable button for user to add payment information
// 4. Retrieve the payment information and call the createpaymentmethod mutation
// 5. disable button again, and refresh page
export default function PaymentMethods({ userUid }) {
const { initPaymentSheet, presentPaymentSheet } = useStripe()
const [disabledButton, setDisabledButton] = useState(false)
const [createIntent, { data, loading, error }] = useMutation(
ADD_PAYMENT_METHOD_INTENT
)
useEffect(() => {
createUserPaymentMethodIntent()
}, [])
const createUserPaymentMethodIntent = () => {
setDisabledButton(true)
createIntent({
variables: {
createUserPaymentMethodSetupIntentInput: {
userUid: userUid,
},
},
})
.then((res) => {
console.log(res)
const clientSecret =
res.data.createUserPaymentMethodSetupIntent.clientSecret
const setupIntentId =
res.data.createUserPaymentMethodSetupIntent.setupIntentId
const ephemeralKeySecret =
res.data.createUserPaymentMethodSetupIntent.ephemeralKeySecret
const customerId =
res.data.createUserPaymentMethodSetupIntent.customerId
initializePaymentSheet(
clientSecret,
setupIntentId,
ephemeralKeySecret,
customerId
)
})
.catch((err) => console.log({ graphqlError: err }))
}
const initializePaymentSheet = (
clientSecret,
setupIntentId,
ephemeralKeySecret,
customerId
) => {
initPaymentSheet({
customerId: customerId,
customerEphemeralKeySecret: ephemeralKeySecret,
setupIntentClientSecret: setupIntentId,
paymentIntentClientSecret: clientSecret,
})
.then((res) => {
console.log(res)
setDisabledButton(false)
})
.catch((err) => console.log("little error.."))
}
const openPaymentSheet = async () => {
setDisabledButton(true)
const { error } = await presentPaymentSheet()
if (error) {
Alert.alert(`Error code: ${error.code}`, error.message)
} else {
Alert.alert(
"Success",
"Your payment method is successfully set up for future payments!"
)
}
}
return (
<ScrollView>
<PaymentMethodList userUid={userUid} />
<WideButton
disabled={disabledButton}
text="Add New Payment Method"
clicked={openPaymentSheet}
/>
</ScrollView>
)
}
someone plz help :(
you might want to check the logs in your Stripe Dashboard (Dashboard -> Developers -> Logs). From there you'll be able to see more info about this error,

Testing graphql subscriptions with k6

Is it possible to test graphql subscriptions using k6 framework?
I tried to do it, but did not have much success. Also tried to do it with k6 websockets, but did not help.
Thanks
Grapqhql Subscription is based on Websockets so this is theoretically possible to implement using k6 WebSocket.
You can also refer to the documentation for subscriptions here.
You can also use the playground and Networks tab in developer tools to figure out the messages/requests that are sent to the server.
Here is how I was able to achieve it:
import ws from "k6/ws";
export default function(){
const url = "ws://localhost:4000/graphql" // replace with your url
const token = null; // replace with your auth token
const operation = `
subscription PostFeed {
postCreated {
author
comment
}
}` // replace with your subscription
const headers = {
"Sec-WebSocket-Protocol": "graphql-ws",
};
if (token != null) Object.assign(headers,{ Authorization: `Bearer ${token}`});
ws.connect(
url,
{
headers,
},
(socket) => {
socket.on("message", (msg) => {
const message = JSON.parse(msg);
if (message.type == "connection_ack")
console.log("Connection Established with WebSocket");
if (message.type == "data") console.log(`Message Received: ${message}`)
});
socket.on("open", () => {
socket.send(
JSON.stringify({
type: "connection_init",
payload: headers,
})
);
socket.send(
JSON.stringify({
type: "start",
payload: {
query: operation,
},
})
);
});
}
);
}
Hope this helps! 🍻

Stripe React Native Subscriptions

I'm stuck on this issue:
When I try to submit this subscription, I got an error of "subscription_payment_intent_requires_action", should I handle it on client-side or there is another way to do it?
For information: I already created setupIntent (verify 3D Secure) and return payment_method
Steps:
1 - create setupIntent
2 - generate payment_method ( verify 3D Secure)
3 - create Customer and attach payment_method to it
4 - create Subscription (on back_end) => error: "subscription_payment_intent_requires_action" shows up!
Thank you all!
// Create Subscription With Payment Method + Customer ID
router.post('/createSubscription', function (req, res){
const {payment_method, customerId} = req.body;
var stripe = require("stripe")(stripe_sk);
try{
stripe.subscriptions
.create({
customer: customerId,
items: [{
plan: 'plan_HTGCI8ljPYFTHQ'
}],
default_payment_method: payment_method,
expand: ["latest_invoice.payment_intent"],
// enable_incomplete_payments: true
}).then(subscription => {
res.send({
subscription : subscription
})
}).catch(err => {
res.send({
err
})
})
} catch (error) {
res.send("Error : ", error);
}
});```

Microsoft Graph sendMail doesn't work and returns NULL

I'm trying to send e-mails with MS Graph 1.0 and I have not any get any result or response. E-Mails haven't been sent and sendMail method don't return any error o message... it only says "null".
My code is based on this example https://github.com/microsoftgraph/msgraph-sdk-javascript#post-and-patch and looks like this:
// Initialize Graph client
const client = graph.Client.init({
authProvider: (done) => {
done(null, accessToken);
}
});
try {
// construct the email object
var mail = {
subject: "Microsoft Graph JavaScript Sample",
toRecipients: [{
emailAddress: {
address: "mail#domain.com"
}
}],
body: {
content: "<h1>MicrosoftGraph JavaScript Sample</h1>Check out https://github.com/microsoftgraph/msgraph-sdk-javascript",
contentType: "html"
}
};
client
.api('/me/sendMail')
.post({message: mail}, (err, res) => {
console.log("---> " + res);
});
console.log("Try ends");
} catch (err) {
parms.message = 'Error retrieving messages';
parms.error = { status: `${err.code}: ${err.message}` };
parms.debug = JSON.stringify(err.body, null, 2);
res.render('error', parms);
}
I guess mail var needs a header, but anyway, API should return me something, right? And, obviously, which is the problem with the email sending?
I finally added rawResponse to .post call and look at err log...
client
.api('/me/sendMail')
.header("Content-type", "application/json")
.post({message: mail}, (err, res, rawResponse) => {
console.log(rawResponse);
console.log(err);
});
... and I could see that I had problem with my authentication token. So, I was using the api correctly and code from the question is ok.

Can't use GitHub for 3rd party authentication in Parse Server

I am trying to setup a simple example of 3rd party authentication in a localhost parse server using GitHub. I read the parse guide, the wiki, as well as old issues and websites (pre- and post- the opensourcing of parse). Almost everything is working, but the last part: the link between the GitHub access token and the Parse.User.
Here's my client and server code.
Client code (using hello.js for connecting with github and getting the access_token):
<html>
<body>
<script src="src/hello.polyfill.js"></script>
<script src="src/hello.js"></script>
<script src="src/modules/github.js"></script>
<script src="https://npmcdn.com/parse/dist/parse.min.js"></script>
<button onclick="hello('github').login()">Login with GitHub</button>
<div id='profile'></div>
<script>
const parseClientID = "[MY_PARSE_APP_ID]";
const githubClientID = "[MY_GITHUB_APP_ID]";
Parse.initialize(parseClientID);
Parse.setURL = "http://localhost:1337/parse";
var provider = {
authenticate(options) {if (options.success) {options.success(this, {});}},
restoreAuthentication(authData) {},
getAuthType() {return 'github';},
deauthenticate() {}
};
let authData = {authData: {access_token: 'REPLACED_ON_THE_FLY', id: githubClientID}};
hello.init({github: githubClientID}, {
oauth_proxy: 'http://localhost:3000/proxy',
redirect_uri: 'http://localhost:3000/redirect'
});
// after loging in, when github calls back, this part of the code tries to
// link the github data with a Parse.User
hello.on('auth.login', (auth) => {
authData.authData.access_token = auth.authResponse.access_token;
var user = new Parse.User();
user._linkWith(provider, authData).then(usr=>console.log(usr), err=>console.log(err));
});
</script>
</body>
</html>
Server code (nothing fancy, standard parse-server route, and oauthshim to talk with hello.js):
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var ParseServer = require('parse-server').ParseServer;
var oauthshim = require('oauth-shim');
var app = express();
app.get('/', (req, res) => {res.render('index');});
app.get('/redirect', (req, res) => {res.render('redirect');});
var api = new ParseServer({
"appId": "[MY_PARSE_APP_ID]",
"masterKey": "[MY_PARSE_MASTER_KEY]",
"appName": "connect",
"databaseURI": "mongodb://127.0.0.1:27017/parse",
"serverURL": "http://localhost:1337/parse",
"auth": {"github": {"id":"[MY_GITHUB_APP_ID]","access_token":"spaceholder"}}
});
app.use('/parse', api);
oauthshim.init([{
client_id: '[MY_GITHUB_APP_ID]',
client_secret: '[MY_GITHUB_SECRET]',
grant_url: 'https://github.com/login/oauth/access_token',
domain: 'http://localhost:3000/redirect'
}]);
app.use('/proxy', oauthshim);
app.listen(1337, function() {console.log('parse-server running on port 1337.');});
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(function(req, res, next) { next(createError(404));});
app.use(function(err, req, res, next) {
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
res.status(err.status || 500);
res.render('error');
});
module.exports = app;
The client displays a single "login" button. On clicking, it connects to github, gets an access token, which is then used to user._linkWith().
At this point, I get this error in the Web console:
error: Github auth is invalid for this user. code=101, message=Github auth is invalid for this user.
I think that I'm not writing the auth object properly, but I can't figure out how to do it just from the Custom Authentication section of the Guide in the parse-server website (https://docs.parseplatform.org/parse-server/guide/#oauth-and-3rd-party-authentication).
Thank you in advance !
I have implemented the github login but not in the same way you did but I think it should work the process should be the same.
You need to get the access token and next with the token get the github user id at https://api.github.com/user. And finaly call the _linkWith method.
On the server side you don't need to add auth configuration. your server should be :
var api = new ParseServer({
"appId": "[MY_PARSE_APP_ID]",
"masterKey": "[MY_PARSE_MASTER_KEY]",
"appName": "connect",
"databaseURI": "mongodb://127.0.0.1:27017/parse",
"serverURL": "http://localhost:1337/parse",
});
And on client client side you don't need to configure the provider. Just call _linkWith like that :
hello.on('auth.login', (auth) => {
// get the github user id before
const authData = {
id: 'your github user id'
access_token: 'your access token'
}
const user = new Parse.User()
return user._linkWith('github', { authData }).then(user => {
// do what you want with user
})
Hope this will help you.

Resources