Skip to content

Commit 5d29577

Browse files
authored
Merge pull request #29 from adreyfus-stripe/store-updates
Updated demo to use new APIs
2 parents 645693b + 7be53fd commit 5d29577

File tree

7 files changed

+234
-146
lines changed

7 files changed

+234
-146
lines changed

README.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,17 @@ There are two articles explaining how this site is set up:
1414

1515
``` bash
1616
# install dependencies
17-
$ yarn install or npm install
17+
$ yarn install or npm run install
1818

1919
# serve with hot reload at localhost:3000
2020
$ yarn dev or npm run dev
2121

2222
# build for production and launch server
23-
$ yarn build or npm build
24-
$ yarn start or npm start
23+
$ yarn build or npm run build
24+
$ yarn start or npm run start
2525

2626
# generate static project
27-
$ yarn generate or npm generate
27+
$ yarn generate or npm run generate
2828
```
2929

3030
For detailed explanation on how things work, checkout [Nuxt.js docs](https://nuxtjs.org).

components/AppCard.vue

+48-10
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,32 @@
99
<label for="card">Credit Card</label>
1010
<br />
1111
<small>
12-
Test using this credit card:
13-
<span class="cc-number">4242 4242 4242 4242</span>, and enter any 5 digits for the zip code
12+
Test using these Stripe test credit card numbers with any CVC, postal code, and expiration date in the future:
13+
<ul>
14+
<li>
15+
<span class="cc-number">4242 4242 4242 4242</span>
16+
</li>
17+
<li>
18+
<span class="cc-number">4000 0027 6000 3184</span> (requires authentication, will trigger a pop-up)
19+
</li>
20+
<li>
21+
<span class="cc-number">4000 0000 0000 9995</span> (will decline with a decline code of insufficient funds)
22+
</li>
23+
</ul>
1424
</small>
1525
<card
1626
class="stripe-card"
1727
id="card"
1828
:class="{ complete }"
19-
stripe="pk_test_5ThYi0UvX3xwoNdgxxxTxxrG"
29+
stripe="pk_test_8ssZgwB2PiH0ajJksD2gVbsG00u7Y3IDPv"
2030
:options="stripeOptions"
2131
@change="complete = $event.complete"
2232
/>
33+
<small class="card-error">{{error}}</small>
2334
<button
2435
class="pay-with-stripe button"
2536
@click="pay"
26-
:disabled="!complete || !stripeEmail"
37+
:disabled="!complete || !stripeEmail || loading"
2738
>Pay with credit card</button>
2839
</div>
2940

@@ -47,7 +58,7 @@
4758
</template>
4859

4960
<script>
50-
import { Card, createToken } from "vue-stripe-elements-plus";
61+
import { Card, handleCardPayment } from "vue-stripe-elements-plus";
5162
5263
import { mapState } from "vuex";
5364
@@ -56,26 +67,53 @@ export default {
5667
computed: {
5768
...mapState(["cartUIStatus"])
5869
},
70+
mounted() {
71+
// create a PaymentIntent on Stripe with order information
72+
this.$store.dispatch("createPaymentIntent");
73+
},
5974
data() {
6075
return {
6176
complete: false,
6277
stripeOptions: {
6378
// you can configure that cc element. I liked the default, but you can
6479
// see https://stripe.com/docs/stripe.js#element-options for details
6580
},
66-
stripeEmail: ""
81+
stripeEmail: "",
82+
error: "",
83+
loading: false
6784
};
6885
},
6986
methods: {
7087
pay() {
71-
createToken().then(data => {
72-
const stripeData = { data, stripeEmail: this.stripeEmail };
73-
this.$store.dispatch("postStripeFunction", stripeData);
88+
// confirms the payment and will automatically display a
89+
// pop-up modal if the purchase requires authentication
90+
this.loading = true;
91+
handleCardPayment(this.$store.getters.clientSecret, {
92+
receipt_email: this.stripeEmail
93+
}).then(result => {
94+
this.loading = false;
95+
if (result.error) {
96+
// show the error to the customer, let them try to pay again
97+
this.error = result.error.message;
98+
setTimeout(() => (this.error = ""), 3000);
99+
} else if (
100+
result.paymentIntent &&
101+
result.paymentIntent.status === "succeeded"
102+
) {
103+
// payment succeeded! show a success message
104+
// there's always a chance your customer closes the browser after the payment process and before this code runs so
105+
// we will use the webhook in handle-payment-succeeded for any business-critical post-payment actions
106+
this.$store.commit("updateCartUI", "success");
107+
setTimeout(this.clearCart, 5000);
108+
} else {
109+
this.error = "Some unknown error occured";
110+
setTimeout(() => (this.error = ""), 3000);
111+
}
74112
});
75113
},
76114
clearCart() {
77115
this.complete = false;
78-
this.$store.commit("clearCartCount");
116+
this.$store.commit("clearCart");
79117
}
80118
}
81119
};

functions/create-payment-intent.js

+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// An endpoint that calculates the order total and creates a
2+
// PaymentIntent on Stripe
3+
4+
require("dotenv").config();
5+
const axios = require("axios");
6+
const stripe = require("stripe")(process.env.STRIPE_SECRET_KEY),
7+
headers = {
8+
"Access-Control-Allow-Origin": "*",
9+
"Access-Control-Allow-Headers": "Content-Type"
10+
};
11+
12+
exports.handler = async (event, context) => {
13+
// CORS
14+
if (event.httpMethod === "OPTIONS") {
15+
return {
16+
statusCode: 200,
17+
headers
18+
};
19+
}
20+
21+
const data = JSON.parse(event.body);
22+
console.log(data);
23+
24+
if (!data.items) {
25+
console.error("List of items to purchase is missing.");
26+
27+
return {
28+
statusCode: 400,
29+
headers,
30+
body: JSON.stringify({
31+
status: "missing information"
32+
})
33+
};
34+
}
35+
36+
// Stripe payment processing begins here
37+
try {
38+
// Always calculate the order amount on your server to prevent customers
39+
// from manipulating the order amount from the client
40+
// Here we will use a simple json file to represent inventory
41+
// but you could replace this with a DB lookup
42+
const storeDatabase = await axios.get(
43+
"https://wizardly-sinoussi-7841d1.netlify.com/storedata.json"
44+
);
45+
46+
const amount = data.items.reduce((prev, item) => {
47+
// lookup item information from "database" and calculate total amount
48+
const itemData = storeDatabase.data.find(
49+
storeItem => storeItem.id === item.id
50+
);
51+
return prev + itemData.price * 100 * item.quantity;
52+
}, 0);
53+
54+
// Create a PaymentIntent on Stripe
55+
// A PaymentIntent represents your customer's intent to pay
56+
// and needs to be confirmed on the client to finalize the payment
57+
const paymentIntent = await stripe.paymentIntents.create({
58+
currency: "usd",
59+
amount: amount,
60+
description: "Order from store"
61+
});
62+
63+
// Send the client_secret to the client
64+
// The client secret has a limited set of permissions that
65+
// let you finalize the payment and update some details from the client
66+
return {
67+
statusCode: 200,
68+
headers,
69+
body: JSON.stringify({
70+
clientSecret: paymentIntent.client_secret
71+
})
72+
};
73+
} catch (err) {
74+
console.log(err);
75+
76+
return {
77+
statusCode: 400,
78+
headers,
79+
body: JSON.stringify({
80+
status: err
81+
})
82+
};
83+
}
84+
};

functions/handle-payment-succeeded.js

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// Webhook that listens for events sent from Stripe
2+
// Requires configuration in the Stripe Dashboard
3+
// For more information read https://stripe.com/docs/webhooks
4+
require("dotenv").config();
5+
6+
const stripe = require("stripe")(process.env.STRIPE_SECRET_KEY);
7+
8+
// Create your webhook in the Stripe dashboard at https://dashboard.stripe.com/webhooks
9+
// Use the secret listed in the "Signing secret" section
10+
const endpointSecret = process.env.STRIPE_WEBHOOK_SECRET;
11+
12+
exports.handler = async (event, context) => {
13+
const sig = event.headers["stripe-signature"];
14+
let stripeEvent;
15+
16+
try {
17+
// Verifies that the event was sent by Stripe and deserializes the event
18+
stripeEvent = stripe.webhooks.constructEvent(
19+
event.body,
20+
sig,
21+
endpointSecret
22+
);
23+
} catch (err) {
24+
return { statusCode: 400 };
25+
}
26+
27+
// Handle the event
28+
switch (stripeEvent.type) {
29+
case "payment_intent.succeeded":
30+
const paymentIntent = stripeEvent.data.object;
31+
console.log('object', paymentIntent)
32+
console.log(
33+
"Payment was successful! Charge information:",
34+
paymentIntent.charges.data.filter(charge => charge.status === "succeeded")
35+
);
36+
break;
37+
case "charge.dispute.created":
38+
const charge = stripeEvent.data.object;
39+
console.log("Someone disputed a payment!");
40+
break;
41+
// ... handle other event types
42+
default:
43+
// Unexpected event type
44+
return { statusCode: 400 };
45+
}
46+
47+
// Return a 200 response to acknowledge receipt of the event
48+
return { statusCode: 200 };
49+
};

functions/index.js

-84
This file was deleted.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
"stripe": "^7.4.0",
2222
"uuid": "^3.3.2",
2323
"vue-star-rating": "^1.6.1",
24-
"vue-stripe-elements-plus": "^0.2.10"
24+
"vue-stripe-elements-plus": "^0.3.2"
2525
},
2626
"devDependencies": {
2727
"nodemon": "^1.18.9"

0 commit comments

Comments
 (0)