1
+ <script setup lang="ts">
2
+ import { onMounted , ref } from ' vue'
3
+
4
+ import { loadStripe , type StripeElements , type Stripe } from " @stripe/stripe-js" ;
5
+
6
+
7
+ interface Products {
8
+ name: string
9
+ price: number ,
10
+ images: string
11
+ }
12
+
13
+ interface Props {
14
+ products: Products []
15
+ redirectUrl: string ,
16
+ applePay? : boolean
17
+ googlePay? : boolean
18
+ }
19
+
20
+ const clientSecret = ref (' ' )
21
+ const props = defineProps <Props >();
22
+
23
+ let elements: StripeElements | null = null ;
24
+ let stripe: Stripe | null = null
25
+ const publicKey = ' '
26
+
27
+ const products = props .products
28
+
29
+ onMounted (async () => {
30
+ stripe = await loadStripe (publicKey );
31
+
32
+ await createPaymentIntent ();
33
+ await loadElements (); // Load both address and payment elements
34
+ });
35
+
36
+ async function createPaymentIntent() {
37
+ const url = ' http://localhost:3008/stripe/create-subscription' ;
38
+
39
+ const body = { amount: 99900 , quantity: 1 };
40
+
41
+ const response = await fetch (url , {
42
+ method: ' POST' ,
43
+ headers: {
44
+ ' Content-Type' : ' application/json' ,
45
+ ' Accept' : ' application/json' ,
46
+ },
47
+ body: JSON .stringify (body ),
48
+ });
49
+
50
+ const paymentIntent: any = await response .json ();
51
+ clientSecret .value = paymentIntent .client_secret ;
52
+ }
53
+
54
+ async function loadElements() {
55
+ if (stripe ) {
56
+ const appearance = { /* appearance options */ };
57
+ const options = { mode: ' billing' };
58
+
59
+ // Create the elements instance once
60
+ elements = stripe .elements ({ clientSecret: clientSecret .value , appearance });
61
+
62
+ // Create and mount the address element
63
+ const addressElement = elements .create (' address' , options );
64
+ addressElement .mount (' #address-element' )
65
+
66
+ // Create and mount the payment element
67
+ const paymentElement = elements .create (' payment' )
68
+ paymentElement .mount (" #payment-element" )
69
+ }
70
+ }
71
+
72
+
73
+ async function handleSubmit() {
74
+ if (stripe && elements ) {
75
+ const { error } = await stripe .confirmPayment ({
76
+ elements ,
77
+ confirmParams: {
78
+ return_url: props .redirectUrl , // Redirect URL after payment
79
+ },
80
+ })
81
+ }
82
+ }
83
+ </script >
84
+
85
+ <template >
86
+ <div class =" bg-gray-50" >
87
+ <div class =" mx-auto max-w-2xl px-4 pb-24 pt-16 sm:px-6 lg:max-w-7xl lg:px-8" >
88
+ <h2 class =" sr-only" >Checkout</h2 >
89
+
90
+ <form class =" lg:grid lg:grid-cols-2 lg:gap-x-12 xl:gap-x-16" >
91
+ <div >
92
+ <div >
93
+ <h2 class =" text-lg font-medium text-gray-900" >Contact information</h2 >
94
+
95
+ <div class =" mt-4" >
96
+ <label for =" email-address" class =" block text-sm font-medium text-gray-700" >Email address</label >
97
+ <div class =" mt-1" >
98
+ <input type =" email" id =" email-address" name =" email-address" autocomplete =" email" class =" block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm p-2 border" >
99
+ </div >
100
+ </div >
101
+ </div >
102
+
103
+ <div class =" mt-10 border-t border-gray-200 pt-10" >
104
+ <h2 class =" text-lg font-medium text-gray-900" >Billing information</h2 >
105
+
106
+ <div id =" address-element" class =" mt-12" >
107
+
108
+ </div >
109
+ </div >
110
+
111
+ <!-- Payment -->
112
+ <div class =" mt-10 border-t border-gray-200 pt-10" >
113
+ <h2 class =" text-lg font-medium text-gray-900" >Payment</h2 >
114
+
115
+ <fieldset class =" mt-4" >
116
+ <legend class =" sr-only" >Payment type</legend >
117
+ <div class =" space-y-4 sm:flex sm:items-center sm:space-x-10 sm:space-y-0" >
118
+ <div class =" flex items-center" >
119
+ <input id =" credit-card" name =" payment-type" type =" radio" checked class =" h-4 w-4 border-gray-300 text-indigo-600 focus:ring-indigo-500 form" >
120
+ <label for =" credit-card" class =" ml-3 block text-sm font-medium text-gray-700" >Credit card</label >
121
+ </div >
122
+
123
+ </div >
124
+ </fieldset >
125
+
126
+ <div class =" mt-6" >
127
+ <div id =" payment-element" class =" border rounded-md bg-gray-50 p-3" />
128
+ </div >
129
+
130
+ </div >
131
+ </div >
132
+
133
+ <!-- Order summary -->
134
+ <div class =" mt-10 lg:mt-0" >
135
+ <h2 class =" text-lg font-medium text-gray-900" >Order summary</h2 >
136
+
137
+ <div class =" mt-4 rounded-lg border border-gray-200 bg-white shadow-sm" >
138
+ <h3 class =" sr-only" >Items in your cart</h3 >
139
+ <ul role =" list" class =" divide-y divide-gray-200" >
140
+ <li class =" flex px-4 py-6 sm:px-6" v-for =" product in products" >
141
+ <div class =" flex-shrink-0" >
142
+ <img :src =" product?.images" alt =" Front of men' ; s Basic Tee in black." class =" w-50 rounded-md" >
143
+ </div >
144
+
145
+ <div class =" ml-6 flex flex-1 flex-col" >
146
+ <div class =" flex" >
147
+ <div class =" min-w-0 flex-1" >
148
+ <h4 class =" text-sm" >
149
+ <a href =" #" class =" font-medium text-gray-700 hover:text-gray-800" >{{ product?.name }}</a >
150
+ </h4 >
151
+ <p class =" mt-1 text-sm text-gray-500" >..</p >
152
+ <p class =" mt-1 text-sm text-gray-500" >..</p >
153
+ </div >
154
+
155
+ <div class =" ml-4 flow-root flex-shrink-0" >
156
+ <button type =" button" class =" -m-2.5 flex items-center justify-center bg-white p-2.5 text-gray-400 hover:text-gray-500" >
157
+ <span class =" sr-only" >Remove</span >
158
+ <svg class =" h-5 w-5" viewBox =" 0 0 20 20" fill =" currentColor" aria-hidden =" true" data-slot =" icon" >
159
+ <path fill-rule =" evenodd" d =" M8.75 1A2.75 2.75 0 0 0 6 3.75v.443c-.795.077-1.584.176-2.365.298a.75.75 0 1 0 .23 1.482l.149-.022.841 10.518A2.75 2.75 0 0 0 7.596 19h4.807a2.75 2.75 0 0 0 2.742-2.53l.841-10.52.149.023a.75.75 0 0 0 .23-1.482A41.03 41.03 0 0 0 14 4.193V3.75A2.75 2.75 0 0 0 11.25 1h-2.5ZM10 4c.84 0 1.673.025 2.5.075V3.75c0-.69-.56-1.25-1.25-1.25h-2.5c-.69 0-1.25.56-1.25 1.25v.325C8.327 4.025 9.16 4 10 4ZM8.58 7.72a.75.75 0 0 0-1.5.06l.3 7.5a.75.75 0 1 0 1.5-.06l-.3-7.5Zm4.34.06a.75.75 0 1 0-1.5-.06l-.3 7.5a.75.75 0 1 0 1.5.06l.3-7.5Z" clip-rule =" evenodd" />
160
+ </svg >
161
+ </button >
162
+ </div >
163
+ </div >
164
+
165
+ <div class =" flex flex-1 items-end justify-between pt-2" >
166
+ <p class =" mt-1 text-sm font-medium text-gray-900" >${{ (product?.price / 100).toFixed(2) }}</p >
167
+
168
+ <div class =" ml-4" >
169
+ <label for =" quantity" class =" sr-only" >Quantity</label >
170
+ <select id =" quantity" name =" quantity" class =" rounded-md border border-gray-300 text-left text-base font-medium text-gray-700 shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 sm:text-sm" >
171
+ <option value =" 1" >1</option >
172
+ <option value =" 2" >2</option >
173
+ <option value =" 3" >3</option >
174
+ <option value =" 4" >4</option >
175
+ <option value =" 5" >5</option >
176
+ <option value =" 6" >6</option >
177
+ <option value =" 7" >7</option >
178
+ <option value =" 8" >8</option >
179
+ </select >
180
+ </div >
181
+ </div >
182
+ </div >
183
+ </li >
184
+
185
+ </ul >
186
+ <dl class =" space-y-6 border-t border-gray-200 px-4 py-6 sm:px-6" >
187
+ <div class =" flex items-center justify-between" >
188
+ <dt class =" text-sm" >Subtotal</dt >
189
+ <dd class =" text-sm font-medium text-gray-900" >$950.00</dd >
190
+ </div >
191
+ <div class =" flex items-center justify-between" >
192
+ <dt class =" text-sm" >Shipping</dt >
193
+ <dd class =" text-sm font-medium text-gray-900" >$45.00</dd >
194
+ </div >
195
+ <div class =" flex items-center justify-between" >
196
+ <dt class =" text-sm" >Taxes</dt >
197
+ <dd class =" text-sm font-medium text-gray-900" >$5.52</dd >
198
+ </div >
199
+ <div class =" flex items-center justify-between border-t border-gray-200 pt-6" >
200
+ <dt class =" text-base font-medium" >Total</dt >
201
+ <dd class =" text-base font-medium text-gray-900" >$999.00</dd >
202
+ </div >
203
+ </dl >
204
+
205
+ <div class =" border-t border-gray-200 px-4 py-6 sm:px-6" >
206
+ <button @click =" handleSubmit" type =" button" class =" w-full rounded-md border border-transparent bg-indigo-600 px-4 py-3 text-base font-medium text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 focus:ring-offset-gray-50" >Confirm order</button >
207
+ </div >
208
+ </div >
209
+ </div >
210
+ </form >
211
+ </div >
212
+ </div >
213
+ </template >
0 commit comments