What’s the `gc-pat` mentioned here: `now secret ad...
# prisma-whats-new
m
n
@martin That's a permanent access token that you can issue in your console
m
Where, @nilan?
n
In the project settings under the "Authentication" tab
this token gives permissions to do everything so keep it secret 🙂
m
Gotcha! Seeing it now. Thanks! 😊
n
You can also revoke these tokens if you need to
👍🏽 1
No worries 🙌
m
Where it says:
Now, when new card details are added to a specific user, we'll run this code by means of the mutation callback
Where do I put the code? Do I create a JS file that’s somehow tied to Zeit? Never worked with Zeit before.
n
The order is a bit confusing, but further down it shows how to deploy and where you obtain the url
m
Ok, thanks.
Did you clone this repo?
m
Sorry, one more. When I enter:
now -e STRIPE_SECRET=@stripe-secret -e GC_PAT=@gc-pat -e ENDPOINT=@endpoint create/
(replacing
stripe-secret
and
gc-pat
, of course) into Terminal, it gives me an error:
Error! Could not read directory /Users/martinadams/create
No, didn’t clone… not yet. Following instructions first.
n
You need a js file to clone, did you put the code in a js file?
You can clone the repo, it's in there
m
Oh I see! Thanks!
Sorry to be bothering you more, @nilan. How do I create a Stripe token, presumably in my app (it uses React)?
n
do you mean the credit card token?
m
Yes
No worries, @nilan. Created a stripe token through the docs. I should trust myself a little more before asking for help 😊
n
oh yea I dropped a sneaky link to their docs, they're amazing 🙂 and please keep going with your questions it helps me a lot!
👍🏽 1
m
@nilan, I performed a
mutation
to create card details, and the node properly shows up in the console. However, nothing in Stripe. Just added you to the Graph.cool example git, which I slightly modified (to match my Model naming convention). https://github.com/ConsciousApps/LifePurposeApp-stripe-new
n
Did you deploy the microservice using
now
and enter the correct url?
m
Yes, verified the correct
mutation callback handler
url: https://lifepurposeapp-create-customer-ekifvzwteb.now.sh
Deployed using this command:
now -e STRIPE_SECRET=@stripe-secret -e GC_PAT=@gc-pat -e ENDPOINT=@endpoint create/
n
Yep let me have a look. As additional info, you can check the deployed code like this: https://lifepurposeapp-create-customer-ekifvzwteb.now.sh/_src
🤗 1
👍🏽 1
m
(Thank you for working late, btw!)
n
Looks like you have a typo.
cartToUser
cardToUser
🙂
You might be able to add logging to your now deploy with this package: https://www.npmjs.com/package/now-logs Even though I didn't use it yet
m
Oh no!!!!! I hate when that happens 😊 wasting your valuable time for a typo! Apologies.
n
let's see if that's the only culprit 🙂
Nope, nothing yet.
Again, node is created, but doesn’t show up in Stripe as a new customer (and I have deleted all customers).
cardToUser
like this is undefined I guess?
should be
const userId = user.id
m
Ok, let’s try.
n
If that doesn't work let's try that fancy log lib 😛
Hallelujah!
😀
n
Yay! 👍
m
Thank you, @nilan!
❤️ 1
Almost there, @nilan (this will probably have to wait until tmrw). Have permission set to
Authenticated
for
Create Node
in both
CardDetails
and
Purchases
Models, as per tutorial. However, the tutorial also says this:
Copy code
{
  allUsers(filter:{AND:[{id:$userId}, {id:$new_userId}]}){id}
}
https://www.graph.cool/docs/tutorials/stripe-payments-with-mutation-callbacks-using-micro-and-now-soiyaquah7#permission-setup Here’s how I have the
mutation
set up:
Copy code
export const createCard = gql`
	mutation($cardToken: String!, $userId: ID!) {
		createCard: createCardDetails(
			cardToken: $cardToken
			cardToUserId: $userId
		) {
			id
		}
	}
`
and currently it gives me an
Insufficient Permissions
GraphQL error. Any thoughts?
n
are you currently logged in as a user?
m
Yes.
It gives me the same error when I set permissions to
Everyone
in the graphcool console.
n
ah
can you check if the card is created either way?
m
Oh, wow! Yes!
n
ah, yea
the permission error is referring to the
id
in the query
the mutation part is going through fine
m
But only worked if permission was set to
Everyone
.
(with permission to
Authenticated
, card was still created both in graphcool and in Stripe, but not assigned to User in graphcool)
Calling the mutation, I have:
Copy code
onToken(token) {
	const cardToken = token.id
	const userId = this.props.data.user.id
	this.props.createCard({ variables: { cardToken, userId } })
		.then(() => {
			this.props.createPurchase({ variables: { userId } })
				.then((data) => {
					console.log(data)
				}).catch((e) => { console.error(e) })
		}).catch((e) => { console.error(e) })
}
n
Copy code
export const createCard = gql`
    mutation($cardToken: String!, $userId: ID!) {
        createCard: createCardDetails(
            cardToken: $cardToken
            cardToUserId: $userId
        ) {
            id
        }
    }
`
here you are querying the id of the card
but there are no permissions that allow anyone to see the id of a card
that's why a permission error is returned instead
m
Gotcha. Being a complete GraphQL beginner, how would I set permission, programmatically?
(Again, I really appreciate your support. I’m aware it’s technically not included in my current dev free pricing—just know, though, that I’m fully intending to launch a bunch of apps on the graphcool platform, so I’ll very likely have multiple apps in the
Startup
pricing category.)
…or, should I just remove the
id
from the `mutation`/`query`? I probably don’t require it anyway.
n
You need to include something in the response. maybe you can include a query for the user, because you are allowed to query that anyway? 🙂
Copy code
mutation($cardToken: String!, $userId: ID!) {
        createCard: createCardDetails(
            cardToken: $cardToken
            cardToUserId: $userId
        ) {
            cardToUser {
              id
            }
        }
    }
m
Ok, trying now...
That worked, @nilan! Also had to modify the
query
for `createPurchase`:
Copy code
export const createPurchase = gql`
	mutation($userId: ID!) {
		createPurchase: createPurchases(
			amount: 999
			description: "Life Purpose App"
			purchaseToUserId: $userId
		) {
			purchaseToUser {
				id
			}
		}
	}
`
Thanks!
n
Gotcha. Being a complete GraphQL beginner, how would I set permission, programmatically?
We are working on an easy way to do that, more information soon 🙂
👍🏽 1
And I'm more than happy to help you out 🙂 Glad that the tutorial could be useful for you
m
Awesome. Well Stripe
customer
was created, but no Stripe
charge
yet. Will debug… Hopefully this will be the last you hear of me regarding Stripe 😊 Have a good night!
n
Haha 🙂 I think it's time to get this
now-logs
up and running
m
Ok 😊
n
I can have a look tomorrow as well 🕵️
👍🏽 1
m
Figured it out, @nilan, with the help of the logs. Basically,
stripeId
wasn’t coming up. And that’s because the front-end code executed faster than the server updated the user with the
stripeId
via the
updateUser
mutation in the server code. When I slowed down the front-end by 2 seconds, it was still not working. Not at 3 seconds, either. 4 seconds worked. But once it failed there, too, so I upped it to 5 seconds to be on the safe side. BEFORE: React front-end:
Copy code
this.props.createCard({ variables: { cardToken, userId } })
	.then(() => {
		this.props.createPurchase({ variables: { userId } })
			.then((data) => {
				console.log(data)
			}).catch((e) => { console.error(e) })
	}).catch((e) => { console.error(e) })
AFTER: React front-end:
Copy code
this.props.createCard({ variables: { cardToken, userId } })
	.then(() => {
		setTimeout(() => {
			this.props.createPurchase({ variables: { userId } })
				.then((data) => {
					console.log(data)
				}).catch((e) => { console.error(e) })
		}, 4000)
	}).catch((e) => { console.error(e) })
However, now I’m realizing that once the purchase has gone through, there’s yet another server-based mutation happening: setting
isPaid
from
false
to
true
in the
updatePurchases
mutation. This is now making me look into subscriptions as a solution rather than clobbering together with bad fixes such as
setTimeout
.
Ich bin noch am arbeiten, jedoch habe ich das Konzept nach ca 6 Stunden zum funktionieren gebracht 😳
Here is the code… however, since I’m a beginner, I probably used a verbose way of going about it. Any tips always appreciated (and I can always add you to the repo, LMK). First, the GraphQL / Apollo functions, ready for export / use.
Copy code
// Step 1: Create customer token
export const createCard = gql`
	mutation($cardToken: String!, $userId: ID!) {
		createCard: createCardDetails(
			cardToken: $cardToken
			cardToUserId: $userId
		) {
			cardToUser {
				id
			}
		}
	}
`

// Step 2: See if mutation callback has assigned Stripe customer ID to user
export const receiveStripeId = gql`
	subscription($userId: ID!) {
		receiveStripeId: updateUser(filter: {
			id: $userId
		}) {
			stripeId
		}
	}
`

// Step 3: Make a purchase
export const createPurchase = gql`
	mutation($userId: ID!) {
		createPurchase: createPurchases(
			amount: 999
			description: "Life Purpose App"
			purchaseToUserId: $userId
		) {
			purchaseToUser {
				id
			}
		}
	}
`

// Step 4: See if purchase was successful
export const checkIfPaid = gql`
	subscription($userId: ID!) {
		checkIfPaid: updatePurchases(filter: {
			purchaseToUser: {
				id: $userId
			}
		}) {
			isPaid
		}
	}
`

// Step 5: If yes, upgrade app
export const upgradeApp = gql`
	mutation ($userId: ID!) {
		upgradeApp: updateUser(
			id: $userId,
			paid: true) {
				id
			}
	}
`
Now, the front-end code. Here, token is received from Stripe
Copy code
onToken(token) {
	const cardToken = token.id
	const userId = this.props.data.user.id
	this.setState({ button: 'lpa-button hidden', loading: 'lpa-loadingwheel' })
	this.props.stripePurchase()
	this.props.createCard({ variables: { cardToken, userId } })
		.then(() => { console.log('Creating customer...') })
		.catch((e) => { console.error(e) })
}
and here, the subscription
Copy code
componentWillReceiveProps(newProps) {
		if (!newProps.data.loading) {
			const userId = this.props.data.user.id
			newProps.data.subscribeToMore({
				document: receiveStripeId,
				variables: { userId },
				updateQuery: (previousState, { subscriptionData }) => {
					const { stripeId } = subscriptionData.data.receiveStripeId
					if (stripeId !== null && !this.state.paymentMade) {
						console.log('Purchasing...')
						this.setState({
							paymentMade: true,
							modalTitle: 'Upgrading...',
							modalText: 'Please hang tight while you’re being upgraded.'
						})
						this.props.createPurchase({ variables: { userId } })
							.then(() => {
								console.log('Purchase complete')
							})
							.catch((e) => { console.error(e) })
					}
				},
				onError: (err) => console.error(err)
			})
			newProps.data.subscribeToMore({
				document: checkIfPaid,
				variables: { userId },
				updateQuery: (previousState, { subscriptionData }) => {
					const { isPaid } = subscriptionData.data.checkIfPaid
					if (isPaid && !this.state.appIsPaid) {
						console.log('Upgrading...')
						this.setState({ appIsPaid: true })
						this.props.upgradeApp({ variables: { userId } })
							.then(() => {
								console.log('App upgraded')
								this.setState({
									modalTitle: 'Upgrade successful',
									modalText: 'Thank you for upgrading!',
									modalActionText: 'Continue',
									loading: 'lpa-loadingwheel hidden'
								})
							})
							.catch(err => console.error(err))
					}
				},
				onError: (err) => console.error(err)
			})
		}
		return null
	}
Looks like this was a 16 h workday with a few 20 minute breaks here and there… 😉
n
Wow that's some amazing work! 💪
We are rolling out our new Subscription API today, so you would need to update your subscription syntax then
I think you are now a Stripe expert 😄 You should write a Blog article 💸
m
@nilan: are you saying that if I had waited a day, I could have saved myself all this work? 😳 ok :) do you have the documentation for the new subscription API?
n
No that's not what I'm saying 🙂 I think the biggest problem you solved was the general Stripe workflow. Hopefully updating the subscription syntax is straight-forward!
💯 1
I did not realize you are going to use subscriptions, otherwise I would have give you a heads-up 🙈
m
Ok cool, thanks! Yeah, you didn't know.
Happy to write a blog article for graphcool
🎉 1
Will the subscription docs be updated later on? Doesn't seem like anything has changed.
n
No, it's already updated to the not yet released API
For example, currently the
createPost
subscription is available
soon, you have to express it like this:
Copy code
subscription newPosts {
  Post(
    filter: {
      mutation_in: [CREATED]
    }
  ) {
    mutation
    node {
      description
      imageUrl
    }
  }
}
it's a different syntax 🙂
About the blog article, let me know how I can help you, sounds exciting 🙂
m
Ok. I saw this yesterday, but the graphcool-example repo had it working differently, so I used that. LMK when the API will be released. Yes, re: blog post. What would you like me to write about?
👍 1
@nilan, just got this message from the browser once I uploaded the app to its custom HTTPS domain:
Copy code
Mixed Content: The page at 'https://' was loaded over HTTPS, but attempted to connect to the insecure WebSocket endpoint '<ws://subscriptions.graph.cool/ciy0lc7u302ov0119p56aari0>'. This request has been blocked; this endpoint must be available over WSS.
n
replace
ws
by
wss
🙂
👍🏽 1
m
K 😊
@nilan, any updates on subscriptions, yet?
n
Hey Martin! The API is rolled out, we'll announce more soon 🙂
m
Meaning, I can already begin using it according to the
docs
?
n
yes!
m
Ok, cool. Thanks!
n
Let me know if you need assistance with the migration
m
Thank you. With the new
subscription
, the
React
code can remain unchanged, right?
Copy code
componentWillReceiveProps(newProps) {
		const { user } = this.props.data
		const userId = user.id
		newProps.data.subscribeToMore({
			document: receiveStripeId,
			variables: { userId },
			updateQuery: (previousState, { subscriptionData }) => {
				const { stripeId } = subscriptionData.data.receiveStripeId
				if (stripeId !== null && !this.state.paymentMade) {
					console.log('Purchasing...')
					this.setState({
						paymentMade: true,
						modalTitle: 'Upgrading...',
						modalText: 'Please hang tight while you’re being upgraded.'
					})
					this.props.createPurchase({ variables: { userId } })
						.then(() => {
							console.log('Purchase complete')
						})
						.catch((e) => { console.error(e) })
				}
			},
			onError: (err) => console.error(err)
		})
	}
n
You will need to update this line:
Copy code
//  const { stripeId } = subscriptionData.data.receiveStripeId
 const { stripeId } = subscriptionData.data.node.receiveStripeId
I believe
m
Ok, thanks.
n
You'll see how it's lined up in the subscription itself, and here you basically have to mirror it 🙂
👍🏽 1