Skip to content

Commit

Permalink
feat: Checkout mutation (hygraph#49)
Browse files Browse the repository at this point in the history
* feat: Add Printful API datasource to Apollo Server

With createOrder method to interact with API

* feat: Update GCMS createOrder mutation to match schema

* feat: Working mutation to create GCMS order on checkout

* feat: Working mutation to create Printful order on checkout

* simplify checkout form

* feat: Add createPaymentIntentResolver

* fix: Return required fields from Stripe

* fix: Actually pass external_id field to Printful order create payload

* feat: Use confirmCardPayment to confirm PaymentIntent

* add Select form element

* feat: Update checkout form to use shipping country listings from Printful

Needs ynnoj/gatsby-source-printful#4

* chore: Bump to gatsby-source-printful@1.2.0

* refactor: Use form state for active shipping, billing countries logic

* refactor: Use input with checkout mutation schema

* chore: bump use-cart pkg
  • Loading branch information
Jonathan Steele authored Dec 16, 2019
1 parent cfa5704 commit 71c92a1
Show file tree
Hide file tree
Showing 14 changed files with 480 additions and 210 deletions.
25 changes: 20 additions & 5 deletions functions/graphql/datasources/graphcms.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,30 @@ const getOrderQuery = gql`
`;

const createOrderMutation = gql`
mutation createOrder($name: String!, $email: String!, $total: Int!) {
createOrder(data: { name: $name, email: $email, total: $total }) {
mutation createOrder(
$name: String!
$email: String!
$phone: String
$total: Int!
$items: [OrderItemCreateWithoutOrderInput!]!
$billingAddress: AddressCreateWithoutBillingOrderInput!
$shippingAddress: AddressCreateWithoutShippingOrderInput!
) {
createOrder(
data: {
name: $name
email: $email
phone: $phone
total: $total
billingAddress: { create: $billingAddress }
shippingAddress: { create: $shippingAddress }
orderItems: { create: $items }
}
) {
id
name
email
total
billingAddress {
name
}
}
}
`;
Expand Down
2 changes: 2 additions & 0 deletions functions/graphql/datasources/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
const GraphCMSAPI = require('./graphcms');
const PrintfulAPI = require('./printful');

const datasources = () => ({
GraphCMSAPI: new GraphCMSAPI(),
PrintfulAPI: new PrintfulAPI(),
});

module.exports = datasources;
31 changes: 31 additions & 0 deletions functions/graphql/datasources/printful.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
const { RESTDataSource } = require('apollo-datasource-rest');

class PrintfulAPI extends RESTDataSource {
constructor() {
super();
this.baseURL = 'https://api.printful.com';
}

willSendRequest(request) {
request.headers.set(
'Authorization',
`Basic ${Buffer.from(process.env.PRINTFUL_API_KEY).toString('base64')}`
);
}

async createOrder({ external_id, items, recipient }) {
try {
const { result: data } = await this.post(`orders`, {
external_id,
items,
recipient,
});

return data;
} catch (err) {
console.error(err);
}
}
}

module.exports = PrintfulAPI;
4 changes: 4 additions & 0 deletions functions/graphql/graphql.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
require('dotenv').config();

const { ApolloServer } = require('apollo-server-lambda');
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);

const typeDefs = require('./typeDefs');
const resolvers = require('./resolvers');
Expand All @@ -10,6 +11,9 @@ const server = new ApolloServer({
typeDefs,
resolvers,
dataSources,
context: () => ({
stripe,
}),
});

exports.handler = server.createHandler();
11 changes: 11 additions & 0 deletions functions/graphql/resolvers/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const order = require('./query/order');

const checkout = require('./mutation/checkout');
const createPaymentIntent = require('./mutation/createPaymentIntent');
const submitReview = require('./mutation/submitReview');

const resolvers = {
Expand All @@ -9,8 +10,18 @@ const resolvers = {
},
Mutation: {
checkout,
createPaymentIntent,
submitReview,
},
PaymentIntentStatus: {
CANCELLED: 'cancelled',
PROCESSING: 'processing',
REQUIRES_ACTION: 'requires_action',
REQUIRES_CAPTURE: 'requires_capture',
REQUIRES_CONFIRMATION: 'requires_confirmation',
REQUIRES_PAYMENT_METHOD: 'requires_payment_method',
SUCCEEDED: 'succeeded',
},
};

module.exports = resolvers;
31 changes: 27 additions & 4 deletions functions/graphql/resolvers/mutation/checkout.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,32 @@
const checkoutResolver = async (_, args, ctx) => {
const checkoutResolver = async (_, { input }, { dataSources }) => {
try {
// placeholder
const order = await ctx.dataSources.GraphCMSAPI.createOrder(args);
const { id: graphCMSOrderId } = await dataSources.GraphCMSAPI.createOrder(
input
);

return order;
const {
country: country_code,
state: state_code,
...rest
} = input.shippingAddress;

const { id: printfulOrderId } = await dataSources.PrintfulAPI.createOrder({
external_id: graphCMSOrderId,
recipient: {
email: input.email,
country_code,
state_code,
...rest,
},
items: input.items.map(
({ quantity, variantId: external_variant_id }) => ({
external_variant_id,
quantity,
})
),
});

return { graphCMSOrderId, printfulOrderId };
} catch (err) {
return err;
}
Expand Down
28 changes: 28 additions & 0 deletions functions/graphql/resolvers/mutation/createPaymentIntent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
const createPaymentIntentResolver = async (
_,
{ input: { email, metadata, total } },
{ stripe }
) => {
try {
const {
id,
client_secret: clientSecret,
status,
} = await stripe.paymentIntents.create({
amount: total,
currency: 'eur',
metadata,
receipt_email: email,
});

return {
id,
clientSecret,
status,
};
} catch (err) {
return err;
}
};

module.exports = createPaymentIntentResolver;
61 changes: 57 additions & 4 deletions functions/graphql/typeDefs.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,81 @@ const typeDefs = gql`
}
type Mutation {
checkout(name: String!, email: String!, total: Int!): Order
checkout(input: CheckoutInput!): Order
createPaymentIntent(input: PaymentIntentInput!): PaymentIntent
submitReview(input: SubmitReviewInput!): Review
}
type Order {
graphCMSOrderId: ID!
printfulOrderId: ID!
}
type PaymentIntent {
id: ID!
name: String!
email: String
total: Int!
clientSecret: String!
status: PaymentIntentStatus!
}
type Review {
id: ID!
}
enum PaymentIntentStatus {
CANCELLED
PROCESSING
REQUIRES_ACTION
REQUIRES_CAPTURE
REQUIRES_CONFIRMATION
REQUIRES_PAYMENT_METHOD
SUCCEEDED
}
input SubmitReviewInput {
name: String!
headline: String!
rating: Int!
message: String
productId: ID!
}
input CheckoutInput {
name: String!
email: String!
phone: String
total: Int!
items: [CheckoutItemInput!]!
shippingAddress: CheckoutAddressInput!
billingAddress: CheckoutAddressInput!
}
input CheckoutItemInput {
name: String!
variantId: ID!
price: Int!
quantity: Int = 1
}
input CheckoutAddressInput {
address1: String!
address2: String
city: String!
country: String!
name: String!
state: String!
zip: String!
}
input PaymentIntentInput {
email: String!
metadata: PaymentIntentMeta!
total: Int!
}
input PaymentIntentMeta {
graphCMSOrderId: ID!
printfulOrderId: ID!
}
`;

module.exports = typeDefs;
8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
},
"dependencies": {
"apollo-datasource-graphql": "1.3.2",
"apollo-datasource-rest": "^0.6.9",
"apollo-server-lambda": "2.9.12",
"classnames": "^2.2.6",
"currency-formatter": "1.5.5",
Expand All @@ -20,7 +21,7 @@
"gatsby-plugin-sharp": "^2.3.3",
"gatsby-plugin-stripe": "1.2.3",
"gatsby-source-graphql": "2.1.20",
"gatsby-source-printful": "1.1.0",
"gatsby-source-printful": "1.2.0",
"gatsby-transformer-sharp": "^2.3.5",
"graphcms-image": "^1.1.0-beta4",
"graphql-hooks": "4.1.1",
Expand All @@ -29,9 +30,10 @@
"query-string": "^6.9.0",
"react": "16.10.2",
"react-dom": "16.10.2",
"react-stripe-elements": "6.0.1",
"react-hook-form": "3.28.14",
"react-use-cart": "1.1.3"
"react-stripe-elements": "6.0.1",
"react-use-cart": "1.2.2",
"stripe": "^7.14.0"
},
"devDependencies": {
"dotenv": "8.2.0",
Expand Down
Loading

0 comments on commit 71c92a1

Please sign in to comment.