Skip to main content

syrup_rail_postgres/subscription_billing_service/
reconciliation.rs

1use super::*;
2
3impl SubscriptionBillingService {
4    /// Applies an already-observed provider outcome without another submission.
5    ///
6    /// Reconciliation enters the same application authority as foreground
7    /// enrollment but reconstructs its secret-free reservation from the exact
8    /// durable attempt and canonical gateway account.
9    pub async fn apply_reconciled_outcome(
10        &self,
11        billing_scope_id: BillingScopeId,
12        attempt_id: PaymentAttemptId,
13        outcome: &GatewayPaymentOutcome,
14    ) -> Result<SubscriptionEnrollmentPaymentResult, SubscriptionBillingServiceError> {
15        let mut transaction = self.pool.begin().await?;
16        let attempt = crate::find_payment_attempt_by_id_in_transaction(
17            &mut transaction,
18            billing_scope_id,
19            attempt_id,
20        )
21        .await?
22        .ok_or(SubscriptionBillingServiceError::InvalidState(
23            "reconciled subscription payment attempt was not found",
24        ))?;
25        transaction.commit().await?;
26        match attempt.kind() {
27            PaymentAttemptKind::SubscriptionInitial => {
28                apply_reconciled_subscription_enrollment_gateway_outcome(
29                    &self.pool,
30                    self.coordinator.as_ref(),
31                    billing_scope_id,
32                    attempt_id,
33                    outcome,
34                )
35                .await
36            }
37            PaymentAttemptKind::SubscriptionRecovery => {
38                apply_reconciled_subscription_recovery_gateway_outcome(
39                    &self.pool,
40                    self.coordinator.as_ref(),
41                    billing_scope_id,
42                    attempt_id,
43                    outcome,
44                )
45                .await
46            }
47            PaymentAttemptKind::SubscriptionRenewal => {
48                apply_reconciled_subscription_renewal_gateway_outcome(
49                    &self.pool,
50                    self.coordinator.as_ref(),
51                    billing_scope_id,
52                    attempt_id,
53                    outcome,
54                )
55                .await
56            }
57            PaymentAttemptKind::SubscriptionPaymentMethodUpdate => {
58                apply_reconciled_subscription_payment_method_replacement_gateway_outcome(
59                    &self.pool,
60                    self.coordinator.as_ref(),
61                    billing_scope_id,
62                    attempt_id,
63                    outcome,
64                )
65                .await
66            }
67            _ => Err(SubscriptionEnrollmentApplicationError::InvalidState(
68                "attempt kind is not owned by the subscription billing service",
69            )),
70        }
71        .map_err(Into::into)
72    }
73}