@openlup/platform 0.0.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +59 -0
- package/LICENSE +204 -0
- package/README.md +471 -1
- package/dist/bff/dispatch.d.ts +27 -0
- package/dist/bff/dispatch.js +47 -0
- package/dist/bff/envelope.d.ts +46 -0
- package/dist/bff/envelope.js +80 -0
- package/dist/bff/health.d.ts +24 -0
- package/dist/bff/health.js +19 -0
- package/dist/bff/index.d.ts +6 -0
- package/dist/bff/index.js +6 -0
- package/dist/bff/lazyRoute.d.ts +4 -0
- package/dist/bff/lazyRoute.js +13 -0
- package/dist/bff/requestContext.d.ts +17 -0
- package/dist/bff/requestContext.js +26 -0
- package/dist/bff/routerRewrite.d.ts +2 -0
- package/dist/bff/routerRewrite.js +31 -0
- package/dist/blob/binding.d.ts +16 -0
- package/dist/blob/binding.js +37 -0
- package/dist/blob/filesystem.d.ts +8 -0
- package/dist/blob/filesystem.js +71 -0
- package/dist/blob/index.d.ts +6 -0
- package/dist/blob/index.js +6 -0
- package/dist/blob/s3.d.ts +32 -0
- package/dist/blob/s3.js +56 -0
- package/dist/blob/s3Client.d.ts +25 -0
- package/dist/blob/s3Client.js +76 -0
- package/dist/blob/supabase.d.ts +40 -0
- package/dist/blob/supabase.js +59 -0
- package/dist/blob/vercel.d.ts +63 -0
- package/dist/blob/vercel.js +69 -0
- package/dist/composition/composeBundle.d.ts +47 -0
- package/dist/composition/composeBundle.js +45 -0
- package/dist/composition/httpBinding.d.ts +8 -0
- package/dist/composition/httpBinding.js +18 -0
- package/dist/composition/index.d.ts +3 -0
- package/dist/composition/index.js +3 -0
- package/dist/composition/nodeHost.d.ts +103 -0
- package/dist/composition/nodeHost.js +145 -0
- package/dist/data/binding.d.ts +34 -0
- package/dist/data/binding.js +92 -0
- package/dist/data/index.d.ts +5 -0
- package/dist/data/index.js +5 -0
- package/dist/data/postgresGateway.d.ts +42 -0
- package/dist/data/postgresGateway.js +135 -0
- package/dist/data/queryBuilder.d.ts +54 -0
- package/dist/data/queryBuilder.js +240 -0
- package/dist/data/queryBuilderSql.d.ts +76 -0
- package/dist/data/queryBuilderSql.js +145 -0
- package/dist/data/supabaseClientFactory.d.ts +34 -0
- package/dist/data/supabaseClientFactory.js +56 -0
- package/dist/data/supabaseGateway.d.ts +22 -0
- package/dist/data/supabaseGateway.js +49 -0
- package/dist/http/deadline.d.ts +20 -0
- package/dist/http/deadline.js +93 -0
- package/dist/http/index.d.ts +6 -0
- package/dist/http/index.js +5 -0
- package/dist/http/requestAdapter.d.ts +38 -0
- package/dist/http/requestAdapter.js +203 -0
- package/dist/http/runtime.d.ts +83 -0
- package/dist/http/runtime.js +199 -0
- package/dist/http/security.d.ts +44 -0
- package/dist/http/security.js +114 -0
- package/dist/http/staticAssets.d.ts +3 -0
- package/dist/http/staticAssets.js +42 -0
- package/dist/http/types.d.ts +23 -0
- package/dist/http/types.js +6 -0
- package/dist/identity/adminAuthBinding.d.ts +28 -0
- package/dist/identity/adminAuthBinding.js +66 -0
- package/dist/identity/adminAuthorization.d.ts +34 -0
- package/dist/identity/adminAuthorization.js +72 -0
- package/dist/identity/adminContracts.d.ts +70 -0
- package/dist/identity/adminContracts.js +43 -0
- package/dist/identity/adminMagicLinkHandler.d.ts +39 -0
- package/dist/identity/adminMagicLinkHandler.js +94 -0
- package/dist/identity/adminMeHandler.d.ts +15 -0
- package/dist/identity/adminMeHandler.js +49 -0
- package/dist/identity/adminPreflight.d.ts +16 -0
- package/dist/identity/adminPreflight.js +38 -0
- package/dist/identity/adminUserActionPorts.d.ts +31 -0
- package/dist/identity/adminUserActionPorts.js +51 -0
- package/dist/identity/adminUserHandlers.d.ts +38 -0
- package/dist/identity/adminUserHandlers.js +140 -0
- package/dist/identity/authVerifyUrl.d.ts +6 -0
- package/dist/identity/authVerifyUrl.js +54 -0
- package/dist/identity/contracts/contracts.d.ts +8 -0
- package/dist/identity/contracts/contracts.js +2 -0
- package/dist/identity/contracts/emailTrust.d.ts +3 -0
- package/dist/identity/contracts/emailTrust.js +4 -0
- package/dist/identity/contracts/index.d.ts +8 -0
- package/dist/identity/contracts/index.js +8 -0
- package/dist/identity/contracts/oidc.d.ts +3 -0
- package/dist/identity/contracts/oidc.js +9 -0
- package/dist/identity/contracts/operatorAuth.d.ts +35 -0
- package/dist/identity/contracts/operatorAuth.js +53 -0
- package/dist/identity/contracts/operatorInvitations.d.ts +37 -0
- package/dist/identity/contracts/operatorInvitations.js +1 -0
- package/dist/identity/contracts/ports.d.ts +22 -0
- package/dist/identity/contracts/ports.js +1 -0
- package/dist/identity/contracts/privacy.d.ts +62 -0
- package/dist/identity/contracts/privacy.js +99 -0
- package/dist/identity/contracts/providerList.d.ts +3 -0
- package/dist/identity/contracts/providerList.js +11 -0
- package/dist/identity/contracts/types.d.ts +16 -0
- package/dist/identity/contracts/types.js +4 -0
- package/dist/identity/contribution.d.ts +8 -0
- package/dist/identity/contribution.js +22 -0
- package/dist/identity/customerAuth.d.ts +8 -0
- package/dist/identity/customerAuth.js +1 -0
- package/dist/identity/customerAuthorization.d.ts +5 -0
- package/dist/identity/customerAuthorization.js +22 -0
- package/dist/identity/customerContracts.d.ts +42 -0
- package/dist/identity/customerContracts.js +43 -0
- package/dist/identity/customerIdentityBinding.d.ts +44 -0
- package/dist/identity/customerIdentityBinding.js +60 -0
- package/dist/identity/customerIdentityVerifier.d.ts +8 -0
- package/dist/identity/customerIdentityVerifier.js +101 -0
- package/dist/identity/customerMagicLinkHandler.d.ts +38 -0
- package/dist/identity/customerMagicLinkHandler.js +115 -0
- package/dist/identity/customerMeHandler.d.ts +11 -0
- package/dist/identity/customerMeHandler.js +46 -0
- package/dist/identity/customerPorts.d.ts +4 -0
- package/dist/identity/customerPorts.js +1 -0
- package/dist/identity/customerSessionBinding.d.ts +38 -0
- package/dist/identity/customerSessionBinding.js +80 -0
- package/dist/identity/customerSessionIssuer.d.ts +10 -0
- package/dist/identity/customerSessionIssuer.js +57 -0
- package/dist/identity/customerVerifyOtpHandler.d.ts +8 -0
- package/dist/identity/customerVerifyOtpHandler.js +41 -0
- package/dist/identity/directOperatorInvitationStore.d.ts +19 -0
- package/dist/identity/directOperatorInvitationStore.js +193 -0
- package/dist/identity/emailTrustPolicy.d.ts +14 -0
- package/dist/identity/emailTrustPolicy.js +16 -0
- package/dist/identity/identityPersonalDataContributor.d.ts +7 -0
- package/dist/identity/identityPersonalDataContributor.js +46 -0
- package/dist/identity/identityReadiness.d.ts +10 -0
- package/dist/identity/identityReadiness.js +25 -0
- package/dist/identity/index.d.ts +67 -0
- package/dist/identity/index.js +66 -0
- package/dist/identity/inviteAdminUserUseCase.d.ts +89 -0
- package/dist/identity/inviteAdminUserUseCase.js +75 -0
- package/dist/identity/jwtLibrary.d.ts +54 -0
- package/dist/identity/jwtLibrary.js +1 -0
- package/dist/identity/machineActorWriteFence.d.ts +4 -0
- package/dist/identity/machineActorWriteFence.js +13 -0
- package/dist/identity/magicLinkPorts.d.ts +28 -0
- package/dist/identity/magicLinkPorts.js +1 -0
- package/dist/identity/managedAccountLinkStore.d.ts +2 -0
- package/dist/identity/managedAccountLinkStore.js +93 -0
- package/dist/identity/managedAdminAuthVerifier.d.ts +7 -0
- package/dist/identity/managedAdminAuthVerifier.js +27 -0
- package/dist/identity/managedAdminUserGateways.d.ts +123 -0
- package/dist/identity/managedAdminUserGateways.js +127 -0
- package/dist/identity/managedClient.d.ts +90 -0
- package/dist/identity/managedClient.js +10 -0
- package/dist/identity/managedClientAuthLink.d.ts +3 -0
- package/dist/identity/managedClientAuthLink.js +115 -0
- package/dist/identity/managedCustomerMe.d.ts +13 -0
- package/dist/identity/managedCustomerMe.js +30 -0
- package/dist/identity/managedEmailTrustPolicy.d.ts +11 -0
- package/dist/identity/managedEmailTrustPolicy.js +105 -0
- package/dist/identity/managedIdentityVerifier.d.ts +2 -0
- package/dist/identity/managedIdentityVerifier.js +16 -0
- package/dist/identity/managedPrivacy.d.ts +4 -0
- package/dist/identity/managedPrivacy.js +39 -0
- package/dist/identity/messages.d.ts +29 -0
- package/dist/identity/messages.js +39 -0
- package/dist/identity/oidcIdentityVerifier.d.ts +45 -0
- package/dist/identity/oidcIdentityVerifier.js +309 -0
- package/dist/identity/oidcPrincipal.d.ts +2 -0
- package/dist/identity/oidcPrincipal.js +10 -0
- package/dist/identity/operatorAdminAuth.d.ts +6 -0
- package/dist/identity/operatorAdminAuth.js +13 -0
- package/dist/identity/operatorAuthHandlers.d.ts +18 -0
- package/dist/identity/operatorAuthHandlers.js +71 -0
- package/dist/identity/operatorIdentityVerifier.d.ts +8 -0
- package/dist/identity/operatorIdentityVerifier.js +19 -0
- package/dist/identity/operatorInvitationLifecycle.d.ts +7 -0
- package/dist/identity/operatorInvitationLifecycle.js +15 -0
- package/dist/identity/operatorInvitationPorts.d.ts +31 -0
- package/dist/identity/operatorInvitationPorts.js +1 -0
- package/dist/identity/operatorPorts.d.ts +37 -0
- package/dist/identity/operatorPorts.js +1 -0
- package/dist/identity/operatorSession.d.ts +13 -0
- package/dist/identity/operatorSession.js +31 -0
- package/dist/identity/operatorTokenAdminAuth.d.ts +15 -0
- package/dist/identity/operatorTokenAdminAuth.js +50 -0
- package/dist/identity/personalDataRegistry.d.ts +4 -0
- package/dist/identity/personalDataRegistry.js +11 -0
- package/dist/identity/platformAdminAuth.d.ts +15 -0
- package/dist/identity/platformAdminAuth.js +1 -0
- package/dist/identity/ports.d.ts +127 -0
- package/dist/identity/ports.js +38 -0
- package/dist/identity/postgresAccountLinkStore.d.ts +7 -0
- package/dist/identity/postgresAccountLinkStore.js +52 -0
- package/dist/identity/postgresCustomerMe.d.ts +3 -0
- package/dist/identity/postgresCustomerMe.js +21 -0
- package/dist/identity/postgresCustomerRecovery.d.ts +11 -0
- package/dist/identity/postgresCustomerRecovery.js +57 -0
- package/dist/identity/postgresOperatorIdentity.d.ts +5 -0
- package/dist/identity/postgresOperatorIdentity.js +137 -0
- package/dist/identity/postgresPrivacy.d.ts +7 -0
- package/dist/identity/postgresPrivacy.js +126 -0
- package/dist/identity/privacyHandlers.d.ts +10 -0
- package/dist/identity/privacyHandlers.js +35 -0
- package/dist/identity/privacyPorts.d.ts +72 -0
- package/dist/identity/privacyPorts.js +8 -0
- package/dist/identity/privacyUseCases.d.ts +5 -0
- package/dist/identity/privacyUseCases.js +55 -0
- package/dist/identity/reconcileAccountForPrincipal.d.ts +20 -0
- package/dist/identity/reconcileAccountForPrincipal.js +66 -0
- package/dist/identity/reconcileAccountHandler.d.ts +11 -0
- package/dist/identity/reconcileAccountHandler.js +30 -0
- package/dist/identity/removeAdminUserUseCase.d.ts +31 -0
- package/dist/identity/removeAdminUserUseCase.js +60 -0
- package/dist/identity/response.d.ts +3 -0
- package/dist/identity/response.js +2 -0
- package/dist/identity/sql.d.ts +44 -0
- package/dist/identity/sql.js +10 -0
- package/dist/job-ledger/index.d.ts +5 -0
- package/dist/job-ledger/index.js +3 -0
- package/dist/job-ledger/ports.d.ts +10 -0
- package/dist/job-ledger/ports.js +7 -0
- package/dist/job-ledger/postgres.d.ts +10 -0
- package/dist/job-ledger/postgres.js +101 -0
- package/dist/job-ledger/sql.d.ts +7 -0
- package/dist/job-ledger/sql.js +10 -0
- package/dist/job-ledger/supabase.d.ts +77 -0
- package/dist/job-ledger/supabase.js +202 -0
- package/dist/kernel/index.d.ts +1 -0
- package/dist/kernel/index.js +1 -0
- package/dist/kernel/platformKernel.d.ts +34 -0
- package/dist/kernel/platformKernel.js +115 -0
- package/dist/migrations/index.d.ts +1 -0
- package/dist/migrations/index.js +1 -0
- package/dist/migrations/postgresRunner.d.ts +50 -0
- package/dist/migrations/postgresRunner.js +176 -0
- package/dist/scheduler/advisoryLock.d.ts +44 -0
- package/dist/scheduler/advisoryLock.js +114 -0
- package/dist/scheduler/cronGuard.d.ts +10 -0
- package/dist/scheduler/cronGuard.js +19 -0
- package/dist/scheduler/cronSecret.d.ts +5 -0
- package/dist/scheduler/cronSecret.js +23 -0
- package/dist/scheduler/handlers.d.ts +9 -0
- package/dist/scheduler/handlers.js +10 -0
- package/dist/scheduler/httpEntry.d.ts +16 -0
- package/dist/scheduler/httpEntry.js +31 -0
- package/dist/scheduler/index.d.ts +9 -0
- package/dist/scheduler/index.js +9 -0
- package/dist/scheduler/jobRegistry.d.ts +21 -0
- package/dist/scheduler/jobRegistry.js +39 -0
- package/dist/scheduler/jobs.d.ts +48 -0
- package/dist/scheduler/jobs.js +92 -0
- package/dist/scheduler/nodeScheduler.d.ts +42 -0
- package/dist/scheduler/nodeScheduler.js +73 -0
- package/dist/scheduler/vercelScheduler.d.ts +29 -0
- package/dist/scheduler/vercelScheduler.js +51 -0
- package/package.json +116 -3
- package/release-gates.json +143 -0
- package/sql/20260809150000_job_run_ledger.sql +301 -0
- package/sql/20260809150001_job_run_ledger_v3.sql +70 -0
- package/sql/20260810180001_self_host_actor_auth.sql +40 -0
- package/sql/20260810204057_customer_identity_read.sql +31 -0
- package/sql/20260814090000_customer_selfservice_recovery.sql +477 -0
- package/sql/20260901143000_admin_membership_authority_foundation.sql +234 -0
- package/sql/20261018120000_identity_operator_invitations.sql +42 -0
- package/sql/20261018180000_identity_operator_challenges.sql +56 -0
- package/sql/20261018190000_identity_privacy_erasures.sql +15 -0
- package/src/bff/dispatch.ts +81 -0
- package/src/bff/envelope.ts +142 -0
- package/src/bff/health.ts +37 -0
- package/src/bff/index.ts +30 -0
- package/src/bff/lazyRoute.ts +28 -0
- package/src/bff/requestContext.ts +48 -0
- package/src/bff/routerRewrite.ts +38 -0
- package/src/blob/binding.ts +55 -0
- package/src/blob/filesystem.ts +86 -0
- package/src/blob/index.ts +17 -0
- package/src/blob/s3.ts +87 -0
- package/src/blob/s3Client.ts +91 -0
- package/src/blob/supabase.ts +88 -0
- package/src/blob/vercel.ts +127 -0
- package/src/composition/composeBundle.ts +109 -0
- package/src/composition/httpBinding.ts +30 -0
- package/src/composition/index.ts +17 -0
- package/src/composition/nodeHost.ts +289 -0
- package/src/data/binding.ts +157 -0
- package/src/data/index.ts +33 -0
- package/src/data/postgresGateway.ts +186 -0
- package/src/data/queryBuilder.ts +300 -0
- package/src/data/queryBuilderSql.ts +183 -0
- package/src/data/supabaseClientFactory.ts +92 -0
- package/src/data/supabaseGateway.ts +76 -0
- package/src/http/deadline.ts +117 -0
- package/src/http/index.ts +36 -0
- package/src/http/requestAdapter.ts +215 -0
- package/src/http/runtime.ts +319 -0
- package/src/http/security.ts +155 -0
- package/src/http/staticAssets.ts +42 -0
- package/src/http/types.ts +31 -0
- package/src/identity/adminAuthBinding.ts +76 -0
- package/src/identity/adminAuthorization.ts +106 -0
- package/src/identity/adminContracts.ts +52 -0
- package/src/identity/adminMagicLinkHandler.ts +132 -0
- package/src/identity/adminMeHandler.ts +64 -0
- package/src/identity/adminPreflight.ts +46 -0
- package/src/identity/adminUserActionPorts.ts +78 -0
- package/src/identity/adminUserHandlers.ts +174 -0
- package/src/identity/authVerifyUrl.ts +56 -0
- package/src/identity/contracts/contracts.ts +15 -0
- package/src/identity/contracts/emailTrust.ts +5 -0
- package/src/identity/contracts/index.ts +8 -0
- package/src/identity/contracts/oidc.ts +10 -0
- package/src/identity/contracts/operatorAuth.ts +61 -0
- package/src/identity/contracts/operatorInvitations.ts +32 -0
- package/src/identity/contracts/ports.ts +25 -0
- package/src/identity/contracts/privacy.ts +86 -0
- package/src/identity/contracts/providerList.ts +13 -0
- package/src/identity/contracts/types.ts +25 -0
- package/src/identity/contribution.ts +29 -0
- package/src/identity/customerAuth.ts +3 -0
- package/src/identity/customerAuthorization.ts +38 -0
- package/src/identity/customerContracts.ts +52 -0
- package/src/identity/customerIdentityBinding.ts +125 -0
- package/src/identity/customerIdentityVerifier.ts +141 -0
- package/src/identity/customerMagicLinkHandler.ts +171 -0
- package/src/identity/customerMeHandler.ts +64 -0
- package/src/identity/customerPorts.ts +2 -0
- package/src/identity/customerSessionBinding.ts +114 -0
- package/src/identity/customerSessionIssuer.ts +71 -0
- package/src/identity/customerVerifyOtpHandler.ts +44 -0
- package/src/identity/directOperatorInvitationStore.ts +178 -0
- package/src/identity/emailTrustPolicy.ts +16 -0
- package/src/identity/identityPersonalDataContributor.ts +48 -0
- package/src/identity/identityReadiness.ts +26 -0
- package/src/identity/index.ts +69 -0
- package/src/identity/inviteAdminUserUseCase.ts +134 -0
- package/src/identity/jwtLibrary.ts +24 -0
- package/src/identity/machineActorWriteFence.ts +19 -0
- package/src/identity/magicLinkPorts.ts +7 -0
- package/src/identity/managedAccountLinkStore.ts +105 -0
- package/src/identity/managedAdminAuthVerifier.ts +40 -0
- package/src/identity/managedAdminUserGateways.ts +164 -0
- package/src/identity/managedClient.ts +58 -0
- package/src/identity/managedClientAuthLink.ts +120 -0
- package/src/identity/managedCustomerMe.ts +52 -0
- package/src/identity/managedEmailTrustPolicy.ts +90 -0
- package/src/identity/managedIdentityVerifier.ts +20 -0
- package/src/identity/managedPrivacy.ts +37 -0
- package/src/identity/messages.ts +41 -0
- package/src/identity/oidcIdentityVerifier.ts +229 -0
- package/src/identity/oidcPrincipal.ts +10 -0
- package/src/identity/operatorAdminAuth.ts +14 -0
- package/src/identity/operatorAuthHandlers.ts +52 -0
- package/src/identity/operatorIdentityVerifier.ts +19 -0
- package/src/identity/operatorInvitationLifecycle.ts +14 -0
- package/src/identity/operatorInvitationPorts.ts +11 -0
- package/src/identity/operatorPorts.ts +11 -0
- package/src/identity/operatorSession.ts +30 -0
- package/src/identity/operatorTokenAdminAuth.ts +79 -0
- package/src/identity/personalDataRegistry.ts +8 -0
- package/src/identity/platformAdminAuth.ts +7 -0
- package/src/identity/ports.ts +158 -0
- package/src/identity/postgresAccountLinkStore.ts +49 -0
- package/src/identity/postgresCustomerMe.ts +33 -0
- package/src/identity/postgresCustomerRecovery.ts +72 -0
- package/src/identity/postgresOperatorIdentity.ts +123 -0
- package/src/identity/postgresPrivacy.ts +117 -0
- package/src/identity/privacyHandlers.ts +33 -0
- package/src/identity/privacyPorts.ts +52 -0
- package/src/identity/privacyUseCases.ts +48 -0
- package/src/identity/reconcileAccountForPrincipal.ts +99 -0
- package/src/identity/reconcileAccountHandler.ts +36 -0
- package/src/identity/removeAdminUserUseCase.ts +85 -0
- package/src/identity/response.ts +3 -0
- package/src/identity/sql.ts +10 -0
- package/src/job-ledger/index.ts +25 -0
- package/src/job-ledger/ports.ts +27 -0
- package/src/job-ledger/postgres.ts +121 -0
- package/src/job-ledger/sql.ts +12 -0
- package/src/job-ledger/supabase.ts +294 -0
- package/src/kernel/index.ts +17 -0
- package/src/kernel/platformKernel.ts +153 -0
- package/src/migrations/index.ts +10 -0
- package/src/migrations/postgresRunner.ts +255 -0
- package/src/scheduler/advisoryLock.ts +145 -0
- package/src/scheduler/cronGuard.ts +30 -0
- package/src/scheduler/cronSecret.ts +26 -0
- package/src/scheduler/handlers.ts +24 -0
- package/src/scheduler/httpEntry.ts +49 -0
- package/src/scheduler/index.ts +47 -0
- package/src/scheduler/jobRegistry.ts +57 -0
- package/src/scheduler/jobs.ts +119 -0
- package/src/scheduler/nodeScheduler.ts +123 -0
- package/src/scheduler/vercelScheduler.ts +71 -0
|
@@ -0,0 +1,477 @@
|
|
|
1
|
+
-- Portable customer self-service recovery forward.
|
|
2
|
+
--
|
|
3
|
+
-- This delta closes the reference bundle's identity-issuance gap and connects
|
|
4
|
+
-- an authenticated customer to two portable recovery actions: communication
|
|
5
|
+
-- consent and repayment of their own still-open order through the existing
|
|
6
|
+
-- captured settlement rail. It stores challenge and recovery token hashes,
|
|
7
|
+
-- never raw credentials, message bodies, recipient addresses, provider
|
|
8
|
+
-- payloads or payment-method artifacts.
|
|
9
|
+
--
|
|
10
|
+
-- The application owns challenge delivery and ES256 signing. PostgreSQL owns
|
|
11
|
+
-- single consumption, principal linking, actor ownership, preference history,
|
|
12
|
+
-- order-token authority and settlement idempotency. The only direct payment
|
|
13
|
+
-- action is the already-published provider-neutral settlement; no gateway,
|
|
14
|
+
-- webhook, saved-card or provider-attempt record is fabricated here.
|
|
15
|
+
--
|
|
16
|
+
-- openlup:allow-security-definer: the three authenticated actor wrappers derive
|
|
17
|
+
-- the principal only from auth.uid() and expose fixed response shapes without
|
|
18
|
+
-- granting customer table DML.
|
|
19
|
+
-- openlup:allow-grant: authenticated receives EXECUTE only on the bounded
|
|
20
|
+
-- preference read/write and recovery-token issue wrappers.
|
|
21
|
+
-- openlup:allow-rls: preference state and events are actor-owned; tables remain
|
|
22
|
+
-- inaccessible and the wrappers are the only customer surface.
|
|
23
|
+
|
|
24
|
+
CREATE TABLE public.customer_identity_challenges (
|
|
25
|
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
26
|
+
client_id uuid NOT NULL REFERENCES public.clients(id) ON DELETE CASCADE,
|
|
27
|
+
principal_id uuid NOT NULL,
|
|
28
|
+
email_fingerprint text NOT NULL,
|
|
29
|
+
token_hash text NOT NULL,
|
|
30
|
+
expires_at timestamptz NOT NULL,
|
|
31
|
+
used_at timestamptz,
|
|
32
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
33
|
+
CONSTRAINT customer_identity_challenges_email_fingerprint_check
|
|
34
|
+
CHECK (email_fingerprint ~ '^[0-9a-f]{64}$'),
|
|
35
|
+
CONSTRAINT customer_identity_challenges_token_hash_check
|
|
36
|
+
CHECK (token_hash ~ '^[0-9a-f]{64}$'),
|
|
37
|
+
CONSTRAINT customer_identity_challenges_expiry_check CHECK (expires_at > created_at),
|
|
38
|
+
CONSTRAINT customer_identity_challenges_token_hash_key UNIQUE (token_hash)
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
CREATE INDEX customer_identity_challenges_client_created_idx
|
|
42
|
+
ON public.customer_identity_challenges (client_id, created_at DESC);
|
|
43
|
+
|
|
44
|
+
CREATE TABLE public.customer_communication_preferences (
|
|
45
|
+
principal_id uuid PRIMARY KEY,
|
|
46
|
+
marketing_newsletter_consent boolean NOT NULL DEFAULT false,
|
|
47
|
+
revision integer NOT NULL DEFAULT 1,
|
|
48
|
+
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
49
|
+
CONSTRAINT customer_communication_preferences_revision_check CHECK (revision > 0)
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
CREATE TABLE public.customer_communication_preference_events (
|
|
53
|
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
54
|
+
principal_id uuid NOT NULL,
|
|
55
|
+
revision integer NOT NULL,
|
|
56
|
+
marketing_newsletter_consent boolean NOT NULL,
|
|
57
|
+
occurred_at timestamptz NOT NULL DEFAULT now(),
|
|
58
|
+
CONSTRAINT customer_communication_preference_events_revision_check CHECK (revision > 0),
|
|
59
|
+
CONSTRAINT customer_communication_preference_events_principal_revision_key
|
|
60
|
+
UNIQUE (principal_id, revision)
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
CREATE TABLE public.commerce_checkout_recovery_tokens (
|
|
64
|
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
65
|
+
order_id uuid NOT NULL REFERENCES public.commerce_orders(id) ON DELETE CASCADE,
|
|
66
|
+
client_id uuid NOT NULL REFERENCES public.clients(id) ON DELETE CASCADE,
|
|
67
|
+
token_hash text NOT NULL,
|
|
68
|
+
expires_at timestamptz NOT NULL,
|
|
69
|
+
revoked_at timestamptz,
|
|
70
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
71
|
+
CONSTRAINT commerce_checkout_recovery_tokens_token_hash_check
|
|
72
|
+
CHECK (token_hash ~ '^[0-9a-f]{64}$'),
|
|
73
|
+
CONSTRAINT commerce_checkout_recovery_tokens_expiry_check CHECK (expires_at > created_at),
|
|
74
|
+
CONSTRAINT commerce_checkout_recovery_tokens_token_hash_key UNIQUE (token_hash)
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
CREATE UNIQUE INDEX commerce_checkout_recovery_tokens_active_order_idx
|
|
78
|
+
ON public.commerce_checkout_recovery_tokens (order_id)
|
|
79
|
+
WHERE revoked_at IS NULL;
|
|
80
|
+
|
|
81
|
+
ALTER TABLE public.customer_communication_preferences ENABLE ROW LEVEL SECURITY;
|
|
82
|
+
ALTER TABLE public.customer_communication_preference_events ENABLE ROW LEVEL SECURITY;
|
|
83
|
+
|
|
84
|
+
REVOKE ALL ON TABLE public.customer_identity_challenges FROM PUBLIC, anon, authenticated;
|
|
85
|
+
REVOKE ALL ON TABLE public.customer_communication_preferences FROM PUBLIC, anon, authenticated;
|
|
86
|
+
REVOKE ALL ON TABLE public.customer_communication_preference_events FROM PUBLIC, anon, authenticated;
|
|
87
|
+
REVOKE ALL ON TABLE public.commerce_checkout_recovery_tokens FROM PUBLIC, anon, authenticated;
|
|
88
|
+
|
|
89
|
+
CREATE FUNCTION public.customer_identity_challenge_issue(
|
|
90
|
+
p_email text,
|
|
91
|
+
p_token_hash text,
|
|
92
|
+
p_expires_at timestamptz,
|
|
93
|
+
p_now timestamptz DEFAULT now()
|
|
94
|
+
)
|
|
95
|
+
RETURNS jsonb
|
|
96
|
+
LANGUAGE plpgsql
|
|
97
|
+
SET search_path = public, pg_catalog
|
|
98
|
+
AS $$
|
|
99
|
+
DECLARE
|
|
100
|
+
v_client public.clients%ROWTYPE;
|
|
101
|
+
v_principal_id uuid;
|
|
102
|
+
v_challenge_id uuid;
|
|
103
|
+
v_email text;
|
|
104
|
+
BEGIN
|
|
105
|
+
v_email := lower(btrim(COALESCE(p_email, '')));
|
|
106
|
+
IF char_length(v_email) < 3 OR char_length(v_email) > 320
|
|
107
|
+
OR p_token_hash !~ '^[0-9a-f]{64}$'
|
|
108
|
+
OR p_expires_at <= p_now OR p_expires_at > p_now + interval '30 minutes' THEN
|
|
109
|
+
RAISE EXCEPTION 'customer_identity_challenge_invalid_input' USING ERRCODE = '22023';
|
|
110
|
+
END IF;
|
|
111
|
+
|
|
112
|
+
SELECT * INTO v_client FROM public.clients
|
|
113
|
+
WHERE lower(email) = v_email
|
|
114
|
+
LIMIT 1;
|
|
115
|
+
IF NOT FOUND THEN
|
|
116
|
+
RETURN jsonb_build_object('deliverable', false);
|
|
117
|
+
END IF;
|
|
118
|
+
|
|
119
|
+
IF EXISTS (
|
|
120
|
+
SELECT 1 FROM public.customer_identity_challenges
|
|
121
|
+
WHERE client_id = v_client.id AND created_at > p_now - interval '60 seconds'
|
|
122
|
+
) THEN
|
|
123
|
+
RETURN jsonb_build_object('deliverable', false);
|
|
124
|
+
END IF;
|
|
125
|
+
|
|
126
|
+
v_principal_id := COALESCE(v_client.principal_id, gen_random_uuid());
|
|
127
|
+
INSERT INTO public.customer_identity_challenges
|
|
128
|
+
(client_id, principal_id, email_fingerprint, token_hash, expires_at, created_at)
|
|
129
|
+
VALUES (
|
|
130
|
+
v_client.id,
|
|
131
|
+
v_principal_id,
|
|
132
|
+
encode(sha256(convert_to(v_email, 'UTF8')), 'hex'),
|
|
133
|
+
p_token_hash,
|
|
134
|
+
p_expires_at,
|
|
135
|
+
p_now
|
|
136
|
+
)
|
|
137
|
+
RETURNING id INTO v_challenge_id;
|
|
138
|
+
|
|
139
|
+
RETURN jsonb_build_object('deliverable', true, 'challengeId', v_challenge_id);
|
|
140
|
+
END;
|
|
141
|
+
$$;
|
|
142
|
+
|
|
143
|
+
CREATE FUNCTION public.customer_identity_challenge_redeem(
|
|
144
|
+
p_token_hash text,
|
|
145
|
+
p_now timestamptz DEFAULT now()
|
|
146
|
+
)
|
|
147
|
+
RETURNS jsonb
|
|
148
|
+
LANGUAGE plpgsql
|
|
149
|
+
SET search_path = public, pg_catalog
|
|
150
|
+
AS $$
|
|
151
|
+
DECLARE
|
|
152
|
+
v_challenge public.customer_identity_challenges%ROWTYPE;
|
|
153
|
+
v_current_principal uuid;
|
|
154
|
+
BEGIN
|
|
155
|
+
IF p_token_hash !~ '^[0-9a-f]{64}$' THEN RETURN NULL; END IF;
|
|
156
|
+
|
|
157
|
+
SELECT * INTO v_challenge FROM public.customer_identity_challenges
|
|
158
|
+
WHERE token_hash = p_token_hash FOR UPDATE;
|
|
159
|
+
IF NOT FOUND OR v_challenge.used_at IS NOT NULL OR v_challenge.expires_at <= p_now THEN
|
|
160
|
+
RETURN NULL;
|
|
161
|
+
END IF;
|
|
162
|
+
|
|
163
|
+
SELECT principal_id INTO v_current_principal FROM public.clients
|
|
164
|
+
WHERE id = v_challenge.client_id FOR UPDATE;
|
|
165
|
+
IF v_current_principal IS NOT NULL AND v_current_principal <> v_challenge.principal_id THEN
|
|
166
|
+
RAISE EXCEPTION 'customer_identity_principal_conflict' USING ERRCODE = '23505';
|
|
167
|
+
END IF;
|
|
168
|
+
|
|
169
|
+
UPDATE public.clients
|
|
170
|
+
SET principal_id = v_challenge.principal_id, updated_at = p_now
|
|
171
|
+
WHERE id = v_challenge.client_id;
|
|
172
|
+
UPDATE public.customer_identity_challenges SET used_at = p_now WHERE id = v_challenge.id;
|
|
173
|
+
RETURN jsonb_build_object(
|
|
174
|
+
'principalId', v_challenge.principal_id,
|
|
175
|
+
'email', lower((SELECT email FROM public.clients WHERE id = v_challenge.client_id)));
|
|
176
|
+
END;
|
|
177
|
+
$$;
|
|
178
|
+
|
|
179
|
+
CREATE FUNCTION public.customer_communication_preferences_as_actor()
|
|
180
|
+
RETURNS jsonb
|
|
181
|
+
LANGUAGE sql
|
|
182
|
+
STABLE
|
|
183
|
+
SECURITY DEFINER
|
|
184
|
+
SET search_path = public, auth, pg_catalog
|
|
185
|
+
AS $$
|
|
186
|
+
SELECT CASE WHEN client.id IS NULL THEN NULL ELSE jsonb_build_object(
|
|
187
|
+
'contractVersion', 'customer.communication_preferences.v1',
|
|
188
|
+
'contact', jsonb_build_object('contactId', client.id, 'email', lower(client.email)),
|
|
189
|
+
'marketingNewsletter', jsonb_build_object(
|
|
190
|
+
'purpose', 'marketing_newsletter',
|
|
191
|
+
'state', CASE
|
|
192
|
+
WHEN preference.principal_id IS NULL THEN 'unknown'
|
|
193
|
+
WHEN preference.marketing_newsletter_consent THEN 'granted'
|
|
194
|
+
ELSE 'denied'
|
|
195
|
+
END,
|
|
196
|
+
'granted', COALESCE(preference.marketing_newsletter_consent, false),
|
|
197
|
+
'source', CASE WHEN preference.principal_id IS NULL THEN NULL ELSE 'customer_communication_preferences' END,
|
|
198
|
+
'reason', CASE
|
|
199
|
+
WHEN preference.principal_id IS NULL THEN NULL
|
|
200
|
+
WHEN preference.marketing_newsletter_consent THEN 'customer_self_service_grant'
|
|
201
|
+
ELSE 'customer_self_service_denial'
|
|
202
|
+
END,
|
|
203
|
+
'capturedAt', preference.updated_at,
|
|
204
|
+
'updatedAt', preference.updated_at)) END
|
|
205
|
+
FROM (SELECT auth.uid() AS principal_id) actor
|
|
206
|
+
LEFT JOIN public.clients client ON client.principal_id = actor.principal_id
|
|
207
|
+
LEFT JOIN public.customer_communication_preferences preference
|
|
208
|
+
ON preference.principal_id = actor.principal_id;
|
|
209
|
+
$$;
|
|
210
|
+
|
|
211
|
+
CREATE FUNCTION public.customer_communication_preferences_set_as_actor(
|
|
212
|
+
p_marketing_newsletter_consent boolean,
|
|
213
|
+
p_now timestamptz DEFAULT now()
|
|
214
|
+
)
|
|
215
|
+
RETURNS jsonb
|
|
216
|
+
LANGUAGE plpgsql
|
|
217
|
+
SECURITY DEFINER
|
|
218
|
+
SET search_path = public, auth, pg_catalog
|
|
219
|
+
AS $$
|
|
220
|
+
DECLARE
|
|
221
|
+
v_principal_id uuid := auth.uid();
|
|
222
|
+
v_revision integer;
|
|
223
|
+
BEGIN
|
|
224
|
+
IF v_principal_id IS NULL OR NOT EXISTS (
|
|
225
|
+
SELECT 1 FROM public.clients WHERE principal_id = v_principal_id
|
|
226
|
+
) THEN
|
|
227
|
+
RETURN NULL;
|
|
228
|
+
END IF;
|
|
229
|
+
|
|
230
|
+
INSERT INTO public.customer_communication_preferences
|
|
231
|
+
(principal_id, marketing_newsletter_consent, revision, updated_at)
|
|
232
|
+
VALUES (v_principal_id, p_marketing_newsletter_consent, 1, p_now)
|
|
233
|
+
ON CONFLICT (principal_id) DO UPDATE
|
|
234
|
+
SET marketing_newsletter_consent = EXCLUDED.marketing_newsletter_consent,
|
|
235
|
+
revision = CASE
|
|
236
|
+
WHEN public.customer_communication_preferences.marketing_newsletter_consent
|
|
237
|
+
IS DISTINCT FROM EXCLUDED.marketing_newsletter_consent
|
|
238
|
+
THEN public.customer_communication_preferences.revision + 1
|
|
239
|
+
ELSE public.customer_communication_preferences.revision
|
|
240
|
+
END,
|
|
241
|
+
updated_at = CASE
|
|
242
|
+
WHEN public.customer_communication_preferences.marketing_newsletter_consent
|
|
243
|
+
IS DISTINCT FROM EXCLUDED.marketing_newsletter_consent
|
|
244
|
+
THEN EXCLUDED.updated_at
|
|
245
|
+
ELSE public.customer_communication_preferences.updated_at
|
|
246
|
+
END
|
|
247
|
+
RETURNING revision INTO v_revision;
|
|
248
|
+
|
|
249
|
+
INSERT INTO public.customer_communication_preference_events
|
|
250
|
+
(principal_id, revision, marketing_newsletter_consent, occurred_at)
|
|
251
|
+
VALUES (v_principal_id, v_revision, p_marketing_newsletter_consent, p_now)
|
|
252
|
+
ON CONFLICT (principal_id, revision) DO NOTHING;
|
|
253
|
+
|
|
254
|
+
RETURN public.customer_communication_preferences_as_actor();
|
|
255
|
+
END;
|
|
256
|
+
$$;
|
|
257
|
+
|
|
258
|
+
CREATE FUNCTION public.customer_checkout_recovery_token_issue_as_actor(
|
|
259
|
+
p_order_id uuid,
|
|
260
|
+
p_token_hash text,
|
|
261
|
+
p_expires_at timestamptz,
|
|
262
|
+
p_now timestamptz DEFAULT now()
|
|
263
|
+
)
|
|
264
|
+
RETURNS uuid
|
|
265
|
+
LANGUAGE plpgsql
|
|
266
|
+
SECURITY DEFINER
|
|
267
|
+
SET search_path = public, auth, pg_catalog
|
|
268
|
+
AS $$
|
|
269
|
+
DECLARE
|
|
270
|
+
v_client_id uuid;
|
|
271
|
+
v_token_id uuid;
|
|
272
|
+
BEGIN
|
|
273
|
+
IF auth.uid() IS NULL OR p_token_hash !~ '^[0-9a-f]{64}$'
|
|
274
|
+
OR p_expires_at <= p_now OR p_expires_at > p_now + interval '24 hours' THEN
|
|
275
|
+
RAISE EXCEPTION 'customer_checkout_recovery_invalid_input' USING ERRCODE = '22023';
|
|
276
|
+
END IF;
|
|
277
|
+
|
|
278
|
+
SELECT order_row.client_id INTO v_client_id
|
|
279
|
+
FROM public.commerce_orders order_row
|
|
280
|
+
JOIN public.clients client ON client.id = order_row.client_id
|
|
281
|
+
WHERE order_row.id = p_order_id
|
|
282
|
+
AND order_row.status = 'pending_payment'
|
|
283
|
+
AND client.principal_id = auth.uid()
|
|
284
|
+
FOR UPDATE OF order_row;
|
|
285
|
+
IF NOT FOUND OR v_client_id IS NULL THEN
|
|
286
|
+
RAISE EXCEPTION 'customer_checkout_recovery_order_not_recoverable' USING ERRCODE = '22023';
|
|
287
|
+
END IF;
|
|
288
|
+
|
|
289
|
+
UPDATE public.commerce_checkout_recovery_tokens
|
|
290
|
+
SET revoked_at = p_now
|
|
291
|
+
WHERE order_id = p_order_id AND revoked_at IS NULL;
|
|
292
|
+
INSERT INTO public.commerce_checkout_recovery_tokens
|
|
293
|
+
(order_id, client_id, token_hash, expires_at, created_at)
|
|
294
|
+
VALUES (p_order_id, v_client_id, p_token_hash, p_expires_at, p_now)
|
|
295
|
+
RETURNING id INTO v_token_id;
|
|
296
|
+
RETURN v_token_id;
|
|
297
|
+
END;
|
|
298
|
+
$$;
|
|
299
|
+
|
|
300
|
+
CREATE FUNCTION public.customer_checkout_recovery_find_as_actor(
|
|
301
|
+
p_order_id uuid DEFAULT NULL,
|
|
302
|
+
p_subscription_id uuid DEFAULT NULL
|
|
303
|
+
)
|
|
304
|
+
RETURNS jsonb
|
|
305
|
+
LANGUAGE sql
|
|
306
|
+
STABLE
|
|
307
|
+
SECURITY DEFINER
|
|
308
|
+
SET search_path = public, auth, pg_catalog
|
|
309
|
+
AS $$
|
|
310
|
+
SELECT jsonb_build_object(
|
|
311
|
+
'orderId', order_row.id,
|
|
312
|
+
'createdAt', order_row.created_at,
|
|
313
|
+
'mode', CASE WHEN order_row.subscription_cycle_id IS NULL THEN 'one_time_order' ELSE 'subscription_cycle' END,
|
|
314
|
+
'subscriptionId', cycle.subscription_id,
|
|
315
|
+
'clientHasLiveOrPendingSubscription', EXISTS (
|
|
316
|
+
SELECT 1 FROM public.subscriptions active_subscription
|
|
317
|
+
WHERE active_subscription.client_id = order_row.client_id
|
|
318
|
+
AND active_subscription.status IN ('active', 'paused')))
|
|
319
|
+
FROM public.commerce_orders order_row
|
|
320
|
+
JOIN public.clients client ON client.id = order_row.client_id
|
|
321
|
+
LEFT JOIN public.subscription_cycles cycle ON cycle.id = order_row.subscription_cycle_id
|
|
322
|
+
WHERE client.principal_id = auth.uid()
|
|
323
|
+
AND order_row.status = 'pending_payment'
|
|
324
|
+
AND ((p_order_id IS NOT NULL AND order_row.id = p_order_id)
|
|
325
|
+
OR (p_order_id IS NULL AND p_subscription_id IS NOT NULL AND cycle.subscription_id = p_subscription_id))
|
|
326
|
+
ORDER BY order_row.created_at DESC
|
|
327
|
+
LIMIT 1;
|
|
328
|
+
$$;
|
|
329
|
+
|
|
330
|
+
CREATE FUNCTION public.customer_has_live_subscription_as_actor()
|
|
331
|
+
RETURNS boolean
|
|
332
|
+
LANGUAGE sql
|
|
333
|
+
STABLE
|
|
334
|
+
SECURITY DEFINER
|
|
335
|
+
SET search_path = public, auth, pg_catalog
|
|
336
|
+
AS $$
|
|
337
|
+
SELECT EXISTS (
|
|
338
|
+
SELECT 1 FROM public.subscriptions subscription
|
|
339
|
+
JOIN public.clients client ON client.id = subscription.client_id
|
|
340
|
+
WHERE client.principal_id = auth.uid()
|
|
341
|
+
AND subscription.status IN ('active', 'paused'));
|
|
342
|
+
$$;
|
|
343
|
+
|
|
344
|
+
CREATE FUNCTION public.customer_checkout_recovery_token_inspect(
|
|
345
|
+
p_token_hash text,
|
|
346
|
+
p_now timestamptz DEFAULT now()
|
|
347
|
+
)
|
|
348
|
+
RETURNS jsonb
|
|
349
|
+
LANGUAGE sql
|
|
350
|
+
STABLE
|
|
351
|
+
SET search_path = public, pg_catalog
|
|
352
|
+
AS $$
|
|
353
|
+
SELECT jsonb_build_object(
|
|
354
|
+
'tokenId', token.id,
|
|
355
|
+
'orderId', token.order_id,
|
|
356
|
+
'clientId', token.client_id,
|
|
357
|
+
'mode', CASE WHEN order_row.subscription_cycle_id IS NULL THEN 'one_time_order' ELSE 'subscription_cycle' END,
|
|
358
|
+
'status', order_row.status,
|
|
359
|
+
'subscriptionId', cycle.subscription_id,
|
|
360
|
+
'tokenState', CASE
|
|
361
|
+
WHEN token.revoked_at IS NOT NULL THEN 'revoked'
|
|
362
|
+
WHEN token.expires_at <= p_now THEN 'expired'
|
|
363
|
+
WHEN order_row.status <> 'pending_payment' THEN 'consumed'
|
|
364
|
+
ELSE 'active'
|
|
365
|
+
END)
|
|
366
|
+
FROM public.commerce_checkout_recovery_tokens token
|
|
367
|
+
JOIN public.commerce_orders order_row ON order_row.id = token.order_id
|
|
368
|
+
LEFT JOIN public.subscription_cycles cycle ON cycle.id = order_row.subscription_cycle_id
|
|
369
|
+
WHERE token.token_hash = p_token_hash;
|
|
370
|
+
$$;
|
|
371
|
+
|
|
372
|
+
CREATE FUNCTION public.customer_checkout_recovery_order(p_order_id uuid)
|
|
373
|
+
RETURNS jsonb
|
|
374
|
+
LANGUAGE sql
|
|
375
|
+
STABLE
|
|
376
|
+
SET search_path = public, pg_catalog
|
|
377
|
+
AS $$
|
|
378
|
+
SELECT jsonb_build_object(
|
|
379
|
+
'orderId', order_row.id,
|
|
380
|
+
'clientId', order_row.client_id,
|
|
381
|
+
'status', order_row.status,
|
|
382
|
+
'mode', CASE WHEN order_row.subscription_cycle_id IS NULL THEN 'one_time_order' ELSE 'subscription_cycle' END,
|
|
383
|
+
'totalMinor', order_row.total_amount_minor,
|
|
384
|
+
'currency', order_row.currency_code,
|
|
385
|
+
'createdAt', order_row.created_at,
|
|
386
|
+
'subscriptionId', cycle.subscription_id,
|
|
387
|
+
'subscriptionCycleId', order_row.subscription_cycle_id,
|
|
388
|
+
'paymentIntentId', intent.id,
|
|
389
|
+
'paymentIntentStatus', CASE intent.status
|
|
390
|
+
WHEN 'created' THEN 'pending'
|
|
391
|
+
WHEN 'settled' THEN 'succeeded'
|
|
392
|
+
ELSE 'failed'
|
|
393
|
+
END,
|
|
394
|
+
'customerEmail', client.email)
|
|
395
|
+
FROM public.commerce_orders order_row
|
|
396
|
+
JOIN public.clients client ON client.id = order_row.client_id
|
|
397
|
+
LEFT JOIN public.subscription_cycles cycle ON cycle.id = order_row.subscription_cycle_id
|
|
398
|
+
LEFT JOIN public.commerce_settlement_intents intent ON intent.order_id = order_row.id
|
|
399
|
+
WHERE order_row.id = p_order_id;
|
|
400
|
+
$$;
|
|
401
|
+
|
|
402
|
+
CREATE FUNCTION public.customer_checkout_recovery_settle(
|
|
403
|
+
p_token_hash text,
|
|
404
|
+
p_idempotency_key text,
|
|
405
|
+
p_now timestamptz DEFAULT now()
|
|
406
|
+
)
|
|
407
|
+
RETURNS jsonb
|
|
408
|
+
LANGUAGE plpgsql
|
|
409
|
+
SET search_path = public, pg_catalog
|
|
410
|
+
AS $$
|
|
411
|
+
DECLARE
|
|
412
|
+
v_token public.commerce_checkout_recovery_tokens%ROWTYPE;
|
|
413
|
+
v_order public.commerce_orders%ROWTYPE;
|
|
414
|
+
v_intent public.commerce_settlement_intents%ROWTYPE;
|
|
415
|
+
v_result jsonb;
|
|
416
|
+
v_external_ref text;
|
|
417
|
+
BEGIN
|
|
418
|
+
IF p_token_hash !~ '^[0-9a-f]{64}$'
|
|
419
|
+
OR char_length(btrim(COALESCE(p_idempotency_key, ''))) < 8 THEN
|
|
420
|
+
RAISE EXCEPTION 'customer_checkout_recovery_settle_invalid_input' USING ERRCODE = '22023';
|
|
421
|
+
END IF;
|
|
422
|
+
|
|
423
|
+
SELECT * INTO v_token FROM public.commerce_checkout_recovery_tokens
|
|
424
|
+
WHERE token_hash = p_token_hash FOR UPDATE;
|
|
425
|
+
IF NOT FOUND OR v_token.revoked_at IS NOT NULL OR v_token.expires_at <= p_now THEN
|
|
426
|
+
RAISE EXCEPTION 'customer_checkout_recovery_token_invalid' USING ERRCODE = '22023';
|
|
427
|
+
END IF;
|
|
428
|
+
|
|
429
|
+
SELECT * INTO v_order FROM public.commerce_orders
|
|
430
|
+
WHERE id = v_token.order_id FOR UPDATE;
|
|
431
|
+
IF NOT FOUND THEN
|
|
432
|
+
RAISE EXCEPTION 'customer_checkout_recovery_order_not_recoverable' USING ERRCODE = '22023';
|
|
433
|
+
END IF;
|
|
434
|
+
|
|
435
|
+
SELECT * INTO v_intent FROM public.commerce_settlement_intents
|
|
436
|
+
WHERE order_id = v_order.id FOR UPDATE;
|
|
437
|
+
IF NOT FOUND THEN
|
|
438
|
+
RAISE EXCEPTION 'customer_checkout_recovery_intent_not_recoverable' USING ERRCODE = '22023';
|
|
439
|
+
END IF;
|
|
440
|
+
|
|
441
|
+
v_external_ref := 'captured-' || v_intent.id::text;
|
|
442
|
+
-- The lower rail reads its append-only transition before checking current
|
|
443
|
+
-- intent/order state. That preserves an exact-key replay after a successful
|
|
444
|
+
-- first call while a different key still fails closed on the settled order.
|
|
445
|
+
v_result := public.commerce_record_settlement(
|
|
446
|
+
p_idempotency_key || ':settle', v_intent.id, 'succeeded', v_external_ref,
|
|
447
|
+
jsonb_build_object('source', 'customer.checkout_recovery.v1'), p_now);
|
|
448
|
+
|
|
449
|
+
RETURN jsonb_build_object(
|
|
450
|
+
'orderId', v_order.id,
|
|
451
|
+
'paymentIntentId', v_intent.id,
|
|
452
|
+
'clientId', v_token.client_id,
|
|
453
|
+
'status', 'paid',
|
|
454
|
+
'paymentAttemptId', NULL,
|
|
455
|
+
'provider', 'hidden_rehearsal',
|
|
456
|
+
'providerPaymentId', v_external_ref,
|
|
457
|
+
'clientAction', jsonb_build_object('kind', 'none'),
|
|
458
|
+
'replayed', COALESCE((v_result->>'replayed')::boolean, false));
|
|
459
|
+
END;
|
|
460
|
+
$$;
|
|
461
|
+
|
|
462
|
+
REVOKE ALL ON FUNCTION public.customer_identity_challenge_issue(text, text, timestamptz, timestamptz) FROM PUBLIC, anon, authenticated;
|
|
463
|
+
REVOKE ALL ON FUNCTION public.customer_identity_challenge_redeem(text, timestamptz) FROM PUBLIC, anon, authenticated;
|
|
464
|
+
REVOKE ALL ON FUNCTION public.customer_checkout_recovery_token_inspect(text, timestamptz) FROM PUBLIC, anon, authenticated;
|
|
465
|
+
REVOKE ALL ON FUNCTION public.customer_checkout_recovery_order(uuid) FROM PUBLIC, anon, authenticated;
|
|
466
|
+
REVOKE ALL ON FUNCTION public.customer_checkout_recovery_settle(text, text, timestamptz) FROM PUBLIC, anon, authenticated;
|
|
467
|
+
REVOKE ALL ON FUNCTION public.customer_communication_preferences_as_actor() FROM PUBLIC, anon, authenticated;
|
|
468
|
+
REVOKE ALL ON FUNCTION public.customer_communication_preferences_set_as_actor(boolean, timestamptz) FROM PUBLIC, anon, authenticated;
|
|
469
|
+
REVOKE ALL ON FUNCTION public.customer_checkout_recovery_token_issue_as_actor(uuid, text, timestamptz, timestamptz) FROM PUBLIC, anon, authenticated;
|
|
470
|
+
REVOKE ALL ON FUNCTION public.customer_checkout_recovery_find_as_actor(uuid, uuid) FROM PUBLIC, anon, authenticated;
|
|
471
|
+
REVOKE ALL ON FUNCTION public.customer_has_live_subscription_as_actor() FROM PUBLIC, anon, authenticated;
|
|
472
|
+
|
|
473
|
+
GRANT EXECUTE ON FUNCTION public.customer_communication_preferences_as_actor() TO authenticated;
|
|
474
|
+
GRANT EXECUTE ON FUNCTION public.customer_communication_preferences_set_as_actor(boolean, timestamptz) TO authenticated;
|
|
475
|
+
GRANT EXECUTE ON FUNCTION public.customer_checkout_recovery_token_issue_as_actor(uuid, text, timestamptz, timestamptz) TO authenticated;
|
|
476
|
+
GRANT EXECUTE ON FUNCTION public.customer_checkout_recovery_find_as_actor(uuid, uuid) TO authenticated;
|
|
477
|
+
GRANT EXECUTE ON FUNCTION public.customer_has_live_subscription_as_actor() TO authenticated;
|