@@ -1,13 +1,43 @@ | |||
<template> | |||
<div> | |||
<h4>hello</h4> | |||
<h4>Billing</h4> | |||
<div id="payment-element"></div> | |||
<div id="message"></div> | |||
<button @click="submit" class="btn btn-primary">Submit</button> | |||
</div> | |||
</template> | |||
<script setup> | |||
import { ref } from "vue" | |||
import { ref, onMounted } from "vue" | |||
let stripe = Stripe(process.env.STRIPE_KEY) | |||
const props = defineProps(["sub"]) | |||
const stripe = Stripe(process.env.STRIPE_KEY) | |||
const options = { clientSecret: props.sub.clientSecret } | |||
const elements = stripe.elements(options) | |||
const payEl = elements.create("payment") | |||
function submit() { | |||
let result = stripe.confirmPayment({ | |||
//`Elements` instance that was used to create the Payment Element | |||
elements, | |||
confirmParams: { | |||
return_url: "https://skouter.net/register", | |||
} | |||
}) | |||
} | |||
onMounted(() => { | |||
checkPayment() | |||
payEl.mount("#payment-element") | |||
}) | |||
</script> | |||
<style scoped> | |||
button.btn { | |||
margin: 10px auto; | |||
display: block; | |||
min-width: 90px; | |||
} | |||
</style> |
@@ -0,0 +1,19 @@ | |||
<template> | |||
<section> | |||
<h4 v-if="status == 'succeeded'">Payment succeeded!</h4> | |||
<h4 v-if="status == 'processing'">Payment still processing. Try again later.</h4> | |||
</section> | |||
</template> | |||
<script setup> | |||
import { ref, onMounted } from "vue" | |||
const props = defineProps(["status"]) | |||
</script> | |||
<style> | |||
h4 { | |||
text-align: center; | |||
} | |||
</style> |
@@ -1,8 +1,9 @@ | |||
<template> | |||
<section class="shadowbox"> | |||
<h2>Register</h2> | |||
<account v-if="!step" :err="err" @submit="create" /> | |||
<billing v-if="step" :err="err" @submit="create" /> | |||
<account v-if="step == 1" :err="err" @submit="create" /> | |||
<billing v-if="step == 2" :err="err" :sub="sub"/> | |||
<completed v-if="step == 3" :err="err" :status="sub.paymentStatus"/> | |||
</section> | |||
</template> | |||
@@ -10,10 +11,18 @@ | |||
import { ref, onMounted } from "vue" | |||
import Account from "./account.vue" | |||
import Billing from "./billing.vue" | |||
import Completed from "./completed.vue" | |||
let err = ref("") | |||
const step = ref(0) | |||
const stripe = Stripe(process.env.STRIPE_KEY) | |||
const step = ref(1) | |||
const token = ref("") | |||
const user = ref(null) | |||
const sub = ref(null) | |||
const clientSecret = new URLSearchParams(window.location.search).get( | |||
'payment_intent_client_secret' | |||
); | |||
function getCookie(name) { | |||
var re = new RegExp(name + "=([^;]+)") | |||
@@ -68,9 +77,17 @@ function intent(user) { | |||
}, | |||
}).then(resp => { | |||
if (resp.ok) { | |||
return resp.json().then(u => { | |||
resp.json().then(s => { | |||
err.value = "" | |||
console.log(u) | |||
console.log(s) | |||
sub.value = s | |||
step.value++ | |||
if (["processing", "succeeded"].includes(s.paymentStatus) && | |||
clientSecret == s.clientSecret) { | |||
step.value++ | |||
} else { | |||
step.value = 0 | |||
} | |||
}) | |||
} else { | |||
resp.text().then( e => err.value = e) | |||
@@ -80,7 +97,7 @@ function intent(user) { | |||
onMounted(() => { | |||
getUser().then( u => { | |||
if (u) intent(u) | |||
if (u) { user.value = u; intent(u) } | |||
}) | |||
}) | |||
</script> | |||
@@ -64,6 +64,7 @@ CREATE TABLE subscription ( | |||
user_id INT, | |||
customer_id VARCHAR(255) NOT NULL, | |||
client_secret VARCHAR(255) NOT NULL, | |||
payment_status VARCHAR(50) NOT NULL, | |||
current_period_end INT DEFAULT 0, | |||
current_period_start INT DEFAULT 0, | |||
PRIMARY KEY (`id`), | |||
@@ -72,6 +72,7 @@ type Subscription struct { | |||
Start int `json:"start"` | |||
End int `json:"end"` | |||
ClientSecret string `json:"clientSecret,omitempty"` | |||
PaymentStatus string `json:"paymentStatus"` | |||
} | |||
type User struct { | |||
@@ -1262,7 +1263,8 @@ func querySub(db *sql.DB, id int) (Subscription, error) { | |||
customer_id, | |||
current_period_end, | |||
current_period_start, | |||
client_secret | |||
client_secret, | |||
payment_status | |||
FROM subscription WHERE id = ? | |||
` | |||
row := db.QueryRow(query, id) | |||
@@ -1274,6 +1276,7 @@ func querySub(db *sql.DB, id int) (Subscription, error) { | |||
&s.End, | |||
&s.Start, | |||
&s.ClientSecret, | |||
&s.PaymentStatus, | |||
) | |||
return s, err | |||
@@ -1290,7 +1293,8 @@ func (user *User) querySub(db *sql.DB) error { | |||
customer_id, | |||
current_period_end, | |||
current_period_start, | |||
client_secret | |||
client_secret, | |||
payment_status | |||
FROM subscription WHERE user_id = ? | |||
` | |||
row := db.QueryRow(query, user.Id) | |||
@@ -1303,6 +1307,7 @@ func (user *User) querySub(db *sql.DB) error { | |||
&user.Sub.End, | |||
&user.Sub.Start, | |||
&user.Sub.ClientSecret, | |||
&user.Sub.PaymentStatus, | |||
) | |||
return err | |||
@@ -1421,9 +1426,10 @@ func (sub *Subscription) insertSub(db *sql.DB) (error) { | |||
customer_id, | |||
current_period_end, | |||
current_period_start, | |||
client_secret | |||
client_secret, | |||
payment_status | |||
) | |||
VALUES (?, ?, ?, ?, ?, ?) | |||
VALUES (?, ?, ?, ?, ?, ?, ?) | |||
RETURNING id | |||
` | |||
@@ -1434,18 +1440,19 @@ func (sub *Subscription) insertSub(db *sql.DB) (error) { | |||
sub.End, | |||
sub.Start, | |||
sub.ClientSecret, | |||
sub.PaymentStatus, | |||
) | |||
err = row.Scan(&sub.Id) | |||
return err | |||
} | |||
func (sub *Subscription) updateSubSecret(db *sql.DB) error { | |||
func (sub *Subscription) updateSub(db *sql.DB) error { | |||
var query string | |||
var err error | |||
query = `UPDATE subscription SET | |||
client_secret = ? | |||
query = `UPDATE subscription | |||
SET client_secret = ?, payment_status = ? | |||
WHERE id = ? | |||
` | |||
@@ -1459,11 +1466,15 @@ func (sub *Subscription) updateSubSecret(db *sql.DB) error { | |||
&stripe.PaymentIntentParams{}) | |||
if err != nil { return err } | |||
_, err = db.Exec(query, | |||
p.ClientSecret, | |||
p.Status, | |||
sub.Id, | |||
) | |||
if err != nil { return err } | |||
sub.ClientSecret = p.ClientSecret | |||
sub.PaymentStatus = string(p.Status) | |||
return err | |||
} | |||
@@ -2972,7 +2983,7 @@ func subscribe(w http.ResponseWriter, db *sql.DB, r *http.Request) { | |||
return | |||
} | |||
} | |||
if user.Sub.Id == 0 { | |||
s, err := createSubscription(user.CustomerId) | |||
if err != nil { | |||
@@ -2986,10 +2997,20 @@ func subscribe(w http.ResponseWriter, db *sql.DB, r *http.Request) { | |||
user.Sub.End = int(s.CurrentPeriodEnd) | |||
user.Sub.Start = int(s.CurrentPeriodStart) | |||
user.Sub.ClientSecret = s.LatestInvoice.PaymentIntent.ClientSecret | |||
user.Sub.PaymentStatus = string(s.LatestInvoice.PaymentIntent.Status) | |||
user.Sub.insertSub(db) | |||
err = user.Sub.insertSub(db) | |||
if err != nil { | |||
http.Error(w, err.Error(), 500) | |||
return | |||
} | |||
json.NewEncoder(w).Encode(user.Sub) | |||
} else { | |||
err = user.Sub.updateSub(db) | |||
if err != nil { | |||
http.Error(w, err.Error(), 500) | |||
return | |||
} | |||
json.NewEncoder(w).Encode(user.Sub) | |||
} | |||