This repository has been archived by the owner on Feb 19, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #59 from nathanfallet/feature/stripe-webhook
Feature/stripe webhook
- Loading branch information
Showing
44 changed files
with
619 additions
and
194 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -154,6 +154,7 @@ cloudflare: | |
|
||
stripe: | ||
key: '' | ||
secret: '' | ||
|
||
# MySQL configuration | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
14 changes: 14 additions & 0 deletions
14
...rc/commonMain/kotlin/me/nathanfallet/suitebde/controllers/webhooks/IWebhooksController.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package me.nathanfallet.suitebde.controllers.webhooks | ||
|
||
import io.ktor.server.application.* | ||
import me.nathanfallet.ktorx.controllers.IUnitController | ||
import me.nathanfallet.ktorx.models.annotations.APIMapping | ||
import me.nathanfallet.ktorx.models.annotations.Path | ||
|
||
interface IWebhooksController : IUnitController { | ||
|
||
@APIMapping | ||
@Path("POST", "/stripe") | ||
suspend fun stripe(call: ApplicationCall) | ||
|
||
} |
54 changes: 54 additions & 0 deletions
54
...src/commonMain/kotlin/me/nathanfallet/suitebde/controllers/webhooks/WebhooksController.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package me.nathanfallet.suitebde.controllers.webhooks | ||
|
||
import com.stripe.model.checkout.Session | ||
import com.stripe.net.Webhook | ||
import io.ktor.server.application.* | ||
import io.ktor.server.request.* | ||
import kotlinx.datetime.Clock | ||
import me.nathanfallet.suitebde.models.stripe.StripeOrder | ||
import me.nathanfallet.suitebde.models.stripe.UpdateStripeOrderPayload | ||
import me.nathanfallet.suitebde.usecases.stripe.ICreateStripeOrderForSessionUseCase | ||
import me.nathanfallet.usecases.models.update.IUpdateChildModelSuspendUseCase | ||
|
||
class WebhooksController( | ||
private val stripeSecret: String, | ||
private val createStripeOrderForSessionUseCase: ICreateStripeOrderForSessionUseCase, | ||
private val updateStripeOrderUseCase: IUpdateChildModelSuspendUseCase<StripeOrder, String, UpdateStripeOrderPayload, String>, | ||
) : IWebhooksController { | ||
|
||
override suspend fun stripe(call: ApplicationCall) { | ||
// Decode event (payload) | ||
val payload = call.receiveText() | ||
val signature = call.request.headers["Stripe-Signature"] | ||
val event = Webhook.constructEvent(payload, signature, stripeSecret) | ||
|
||
// Decode event data | ||
val stripeObject = event.dataObjectDeserializer.`object`.get() | ||
when (event.type) { | ||
"checkout.session.completed" -> { | ||
val session = stripeObject as Session | ||
createStripeOrderForSessionUseCase(session) ?: return | ||
if (session.paymentStatus == "paid") updateStripeOrderUseCase( | ||
session.id, | ||
UpdateStripeOrderPayload(Clock.System.now()), | ||
session.metadata["associationId"]!! | ||
) | ||
} | ||
|
||
"checkout.session.async_payment_succeeded" -> { | ||
val session = stripeObject as Session | ||
updateStripeOrderUseCase( | ||
session.id, | ||
UpdateStripeOrderPayload(Clock.System.now()), | ||
session.metadata["associationId"]!! | ||
) | ||
} | ||
|
||
"checkout.session.async_payment_failed" -> { | ||
val session = stripeObject as Session | ||
// Payment failed | ||
} | ||
} | ||
} | ||
|
||
} |
12 changes: 12 additions & 0 deletions
12
...end/src/commonMain/kotlin/me/nathanfallet/suitebde/controllers/webhooks/WebhooksRouter.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package me.nathanfallet.suitebde.controllers.webhooks | ||
|
||
import me.nathanfallet.ktorx.routers.api.APIUnitRouter | ||
|
||
class WebhooksRouter( | ||
controller: IWebhooksController, | ||
) : APIUnitRouter( | ||
controller, | ||
IWebhooksController::class, | ||
route = "webhooks", | ||
prefix = "/api/v1" | ||
) |
73 changes: 0 additions & 73 deletions
73
...anfallet/suitebde/database/associations/StripeAccountsInAssociationsDatabaseRepository.kt
This file was deleted.
Oops, something went wrong.
10 changes: 5 additions & 5 deletions
10
...ociations/StripeAccountsInAssociations.kt → ...uitebde/database/stripe/StripeAccounts.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
73 changes: 73 additions & 0 deletions
73
...nMain/kotlin/me/nathanfallet/suitebde/database/stripe/StripeAccountsDatabaseRepository.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
package me.nathanfallet.suitebde.database.stripe | ||
|
||
import me.nathanfallet.suitebde.models.stripe.CreateStripeAccountPayload | ||
import me.nathanfallet.suitebde.models.stripe.StripeAccount | ||
import me.nathanfallet.suitebde.models.stripe.UpdateStripeAccountPayload | ||
import me.nathanfallet.suitebde.repositories.stripe.IStripeAccountsRepository | ||
import me.nathanfallet.surexposed.database.IDatabase | ||
import me.nathanfallet.usecases.context.IContext | ||
import org.jetbrains.exposed.sql.* | ||
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq | ||
|
||
class StripeAccountsDatabaseRepository( | ||
private val database: IDatabase, | ||
) : IStripeAccountsRepository { | ||
|
||
init { | ||
database.transaction { | ||
SchemaUtils.create(StripeAccounts) | ||
} | ||
} | ||
|
||
override suspend fun list(parentId: String, context: IContext?): List<StripeAccount> = | ||
database.suspendedTransaction { | ||
StripeAccounts | ||
.selectAll() | ||
.where { StripeAccounts.associationId eq parentId } | ||
.map(StripeAccounts::toStripeAccount) | ||
} | ||
|
||
override suspend fun create( | ||
payload: CreateStripeAccountPayload, | ||
parentId: String, | ||
context: IContext?, | ||
): StripeAccount? = | ||
database.suspendedTransaction { | ||
StripeAccounts.insert { | ||
it[associationId] = parentId | ||
it[accountId] = payload.accountId | ||
it[chargesEnabled] = payload.chargesEnabled | ||
}.resultedValues?.map(StripeAccounts::toStripeAccount)?.singleOrNull() | ||
} | ||
|
||
override suspend fun get(id: String, parentId: String, context: IContext?): StripeAccount? = | ||
database.suspendedTransaction { | ||
StripeAccounts | ||
.selectAll() | ||
.where { | ||
StripeAccounts.accountId eq id and (StripeAccounts.associationId eq parentId) | ||
} | ||
.map(StripeAccounts::toStripeAccount) | ||
.singleOrNull() | ||
} | ||
|
||
override suspend fun update( | ||
id: String, | ||
payload: UpdateStripeAccountPayload, | ||
parentId: String, | ||
context: IContext?, | ||
): Boolean = | ||
database.suspendedTransaction { | ||
StripeAccounts.update({ StripeAccounts.accountId eq id and (StripeAccounts.associationId eq parentId) }) { | ||
it[chargesEnabled] = payload.chargesEnabled | ||
} == 1 | ||
} | ||
|
||
override suspend fun delete(id: String, parentId: String, context: IContext?): Boolean = | ||
database.suspendedTransaction { | ||
StripeAccounts.deleteWhere { | ||
accountId eq id and (associationId eq parentId) | ||
} == 1 | ||
} | ||
|
||
} |
29 changes: 29 additions & 0 deletions
29
...de-backend/src/commonMain/kotlin/me/nathanfallet/suitebde/database/stripe/StripeOrders.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package me.nathanfallet.suitebde.database.stripe | ||
|
||
import kotlinx.datetime.toInstant | ||
import me.nathanfallet.suitebde.models.application.SuiteBDEJson | ||
import me.nathanfallet.suitebde.models.stripe.StripeOrder | ||
import org.jetbrains.exposed.sql.ResultRow | ||
import org.jetbrains.exposed.sql.Table | ||
|
||
object StripeOrders : Table() { | ||
|
||
val sessionId = varchar("session_id", 255) | ||
val associationId = varchar("association_id", 32).index() | ||
val email = varchar("email", 255) | ||
val items = text("items") | ||
val paidAt = varchar("paid_at", 255).nullable() | ||
|
||
override val primaryKey = PrimaryKey(sessionId) | ||
|
||
fun toStripeOrder( | ||
row: ResultRow, | ||
) = StripeOrder( | ||
row[sessionId], | ||
row[associationId], | ||
row[email], | ||
SuiteBDEJson.json.decodeFromString(row[items]), | ||
row[paidAt]?.toInstant() | ||
) | ||
|
||
} |
56 changes: 56 additions & 0 deletions
56
...monMain/kotlin/me/nathanfallet/suitebde/database/stripe/StripeOrdersDatabaseRepository.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
package me.nathanfallet.suitebde.database.stripe | ||
|
||
import kotlinx.serialization.encodeToString | ||
import me.nathanfallet.suitebde.models.application.SuiteBDEJson | ||
import me.nathanfallet.suitebde.models.stripe.CreateStripeOrderPayload | ||
import me.nathanfallet.suitebde.models.stripe.StripeOrder | ||
import me.nathanfallet.suitebde.models.stripe.UpdateStripeOrderPayload | ||
import me.nathanfallet.suitebde.repositories.stripe.IStripeOrdersRepository | ||
import me.nathanfallet.surexposed.database.IDatabase | ||
import me.nathanfallet.usecases.context.IContext | ||
import org.jetbrains.exposed.sql.* | ||
|
||
class StripeOrdersDatabaseRepository( | ||
private val database: IDatabase, | ||
) : IStripeOrdersRepository { | ||
|
||
init { | ||
database.transaction { | ||
SchemaUtils.create(StripeOrders) | ||
} | ||
} | ||
|
||
override suspend fun get(id: String, parentId: String, context: IContext?): StripeOrder? = | ||
database.suspendedTransaction { | ||
StripeOrders | ||
.selectAll() | ||
.where { | ||
StripeOrders.sessionId eq id and (StripeOrders.associationId eq parentId) | ||
} | ||
.map(StripeOrders::toStripeOrder) | ||
.singleOrNull() | ||
} | ||
|
||
override suspend fun create(payload: CreateStripeOrderPayload, parentId: String, context: IContext?): StripeOrder? = | ||
database.suspendedTransaction { | ||
StripeOrders.insert { | ||
it[sessionId] = payload.sessionId | ||
it[associationId] = parentId | ||
it[email] = payload.email | ||
it[items] = SuiteBDEJson.json.encodeToString(payload.items) | ||
}.resultedValues?.map(StripeOrders::toStripeOrder)?.singleOrNull() | ||
} | ||
|
||
override suspend fun update( | ||
id: String, | ||
payload: UpdateStripeOrderPayload, | ||
parentId: String, | ||
context: IContext?, | ||
): Boolean = | ||
database.suspendedTransaction { | ||
StripeOrders.update({ StripeOrders.sessionId eq id and (StripeOrders.associationId eq parentId) }) { | ||
it[paidAt] = payload.paidAt?.toString() | ||
} == 1 | ||
} | ||
|
||
} |
Oops, something went wrong.