Stripe での支払いとサブスクリプション
のようなSaaSプラットフォームでは イベントをプレイする、 支払い管理は単なる付属モジュールではなく、 ビジネスモデル。各ユーザーは、料金プランを選択し、料金プランに加入できる必要があります。 サブスクリプション、自動請求書の受け取り、支払い方法の完全な管理 自主性。これを実現するために、プラットフォームは次のものと統合されます。 ストライプ、SaaS 世界で最も利用されている決済プロバイダーであり、 チェックアウト、サブスクリプション、カスタマー ポータル、Webhook 用の API。
この記事では、決済システムのアーキテクチャを以下から詳細に分析します。 管理を通じて、サブスクリプション ステート マシンへの料金プランの定義 支払い失敗、プロモーション コード、イベント資金調達システムなど。
この記事でわかること
- Stripe との統合アーキテクチャ (顧客、サブスクリプション、価格 ID)
- カテゴリ別のサブスクリプション プラン (プライベート、ビジネス、協会)
- 月次および年次の請求サイクル
- サブスクリプション ステート マシン (PENDING → ACTIVE → CANCELLED/SUSPENDED/EXPIRED)
- 3 回の自動再試行による支払い失敗の管理
- サブスクリプションを作成するためのファクトリーメソッド (ストライプ、フリー、バイパス)
- 支払いバイパスと永久バイパスを備えたプロモーション コード システム
- リアルタイム更新のための Stripe Webhook 管理
- イベント購入と資金調達・クラウドファンディングシステム
ストライプ統合アーキテクチャ
Stripe との統合は、正確なアーキテクチャ パターンに従います。バックエンドは次のように機能します。 オーケストレーター 秘密鍵を公開することなく、ユーザーと Stripe API の間でやり取りを行うことができます。 フロントエンドに。コミュニケーション フローは、今後提供される 3 つの核となる Stripe コンセプトに基づいています。 アプリケーションドメインにマッピングされます。
統合の 3 つの柱
- お客様: プラットフォームに登録されている各ユーザーはオブジェクトに関連付けられています
CustomerStripe 上で、によって識別されるidClienteStripe。これにより、Stripe は支払い方法と取引履歴を追跡できるようになります。 - サブスクリプション: ユーザーが有料プランにサインアップすると、
SubscriptionStripe 上で、によって識別されるidAbbonamentoStripe。サブスクリプションは自動請求サイクルを管理します - 価格ID: 各料金プランは、
Priceストライプ経由でidPrezzoStripe。価格 ID は、金額、通貨、請求間隔を定義します。
@Entity
@Table(name = "abbonamenti")
public class Abbonamento {
// Collegamento utente
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "utente_id", nullable = false)
private User utente;
// Dettagli Stripe
@Column(name = "id_cliente_stripe")
private String idClienteStripe; // cus_xxxxxxxxxxxx
@Column(name = "id_abbonamento_stripe")
private String idAbbonamentoStripe; // sub_xxxxxxxxxxxx
@Column(name = "id_prezzo_stripe")
private String idPrezzoStripe; // price_xxxxxxxxxxxx
// Piano e fatturazione
@Enumerated(EnumType.STRING)
private TipoPiano tipoPiano;
@Enumerated(EnumType.STRING)
private CicloPagamento cicloFatturazione;
private BigDecimal importo;
private String valuta = "EUR";
}
StripeService サービス: API とのブリッジ
Il StripeService すべてをカプセル化するインフラストラクチャ コンポーネントです。
Stripe API との対話。 Stripe ID と Price ID 間の双方向マッピングを管理します
内部価格プラン。チェックアウト セッションの作成と解決の両方が可能
Webhook から始まる計画の逆です。
@Service
public class StripeService {
// Mappa: "PRIVATE_STARTER_MENSILE" -> "price_xxxxx"
private final Map<String, String> stripePriceIds = new HashMap<>();
// Mappa inversa: "price_xxxxx" -> "PRIVATE_STARTER_MENSILE"
private final Map<String, String> priceIdToPlanMap = new HashMap<>();
public String getPriceId(TipoPiano tipoPiano, CicloPagamento ciclo) {
String key = tipoPiano.name() + "_" + ciclo.name();
return stripePriceIds.get(key);
}
public TipoPiano getTipoPianoFromPriceId(String priceId) {
String planKey = priceIdToPlanMap.get(priceId);
// Parsing: "PRIVATE_STARTER_MENSILE" -> "PRIVATE_STARTER"
String tipoPianoStr = planKey.endsWith("_MENSILE")
? planKey.substring(0, planKey.length() - "_MENSILE".length())
: planKey.substring(0, planKey.length() - "_ANNUALE".length());
return TipoPiano.valueOf(tipoPianoStr);
}
}
定期購入プラン
イベントをプレイする
3 つのアカウント カテゴリに分けられた料金体系があり、それぞれにプランが用意されています
ますます機能が充実していきます。列挙型 TipoPiano 定義する
利用可能なすべてのプランとその価格、制限、アカウント タイプとの互換性。
プライベートプラン(個人)
- PRIVATE_FREE: 基本制限付きの無料プラン(3イベント、参加者50名)。プラットフォームを試してみたい人に最適
- PRIVATE_STARTER: 月額 4.99 ユーロまたは年間 49.90 ユーロ — 10 イベント、100 人の参加者
- PRIVATE_PRO: 月額 9.99 ユーロまたは年間 99.90 ユーロ — 無制限のイベント、300 人の参加者、AI 分析
- プライベート_プレミアム: 月額 19.99 ユーロまたは年間 199.90 ユーロ — 無制限のイベント、1000 人の参加者、フル機能
事業計画(企業)
- BUSINESS_STARTER: 月額 29 ユーロまたは年間 290 ユーロ — 無制限のイベント、200 人の参加者、5 人のチーム
- ビジネス_プロフェッショナル: 月額 79 ユーロまたは年間 790 ユーロ — 無制限のイベント、参加者 500 名、チーム メンバー 15 名
- BUSINESS_ENTERPRISE: 月額 199 ユーロまたは年間 1990 ユーロ — 無制限のイベント、無制限の参加者、優先サポート
協会プラン(非営利)
- 無制限の関連付け: 年間 24 ユーロ — 無制限のイベントと参加者がすべて含まれる単一プラン。第三セクターを支援するための倫理的な選択
public enum TipoPiano {
PRIVATE_FREE("Private Free", TipoAccount.INDIVIDUO,
BigDecimal.ZERO, BigDecimal.ZERO, 3, 50),
PRIVATE_STARTER("Private Starter", TipoAccount.INDIVIDUO,
new BigDecimal("4.99"), new BigDecimal("49.90"), 10, 100),
PRIVATE_PRO("Private Pro", TipoAccount.INDIVIDUO,
new BigDecimal("9.99"), new BigDecimal("99.90"), -1, 300),
BUSINESS_ENTERPRISE("Business Enterprise", TipoAccount.AZIENDA,
new BigDecimal("199.00"), new BigDecimal("1990.00"), -1, -1),
ASSOCIAZIONE_ILLIMITATO("Associazione Illimitato", TipoAccount.ASSOCIAZIONE,
new BigDecimal("24.00"), new BigDecimal("24.00"), -1, -1);
// -1 = illimitati
private final int maxEventi;
private final int maxPartecipanti;
public boolean isCompatibileCon(TipoAccount tipoAccount) {
return this.tipoAccountRichiesto == tipoAccount;
}
public BigDecimal calcolaRisparmioAnnuale() {
BigDecimal costoAnnualeMensile = prezzoMensile.multiply(new BigDecimal("12"));
BigDecimal risparmio = costoAnnualeMensile.subtract(prezzoAnnuale);
return risparmio.divide(costoAnnualeMensile, 4, RoundingMode.HALF_UP)
.multiply(new BigDecimal("100"));
}
}
年間プランで節約
すべての有料プランでは、以下と同等のサービスが提供されます。 2ヶ月無料 と
年間請求。方法 calcolaRisparmioAnnuale() 動的に計算する
12 か月支払いの場合の年間コストと比較した節約率 (~16.7%)。
請求サイクル
システムは、列挙型によって定義される 2 つの請求サイクルをサポートします。 CicloPagamento:
毎月 e 年間。サイクルの選択は直接影響します
Stripe 上の関連する価格 ID と次回の請求日。
public enum CicloPagamento {
MENSILE("Mensile"),
ANNUALE("Annuale");
private final String displayName;
}
// La chiave di lookup per il Price ID Stripe
// combina TipoPiano + CicloPagamento:
// Es: "PRIVATE_STARTER_MENSILE" -> "price_1Abc..."
// Es: "PRIVATE_STARTER_ANNUALE" -> "price_2Def..."
サブスクリプション ステート マシン
各サブスクリプションは、ライフサイクル中に明確に定義された一連の状態を経ます。
列挙型 StatoAbbonamento このステート マシンを 5 つの可能な値でモデル化します。
それぞれに、どのトランジションが許可され、どのような機能があるかについての正確なルールが設定されています。
アクセス可能。
┌──────────────────────────────────────────────────┐
│ │
│ MACCHINA A STATI ABBONAMENTO │
│ │
└──────────────────────────────────────────────────┘
┌─────────────┐ pagamento riuscito ┌──────────────┐
│ PENDING │ ──────────────────────────▶ │ ACTIVE │
│ (In Attesa) │ │ (Attivo) │
└─────────────┘ └──────┬───────┘
│
┌─────────────────────────────────┼───────────────────┐
│ │ │
annulla() 3 pagamenti falliti segnaScaduto()
│ │ │
▼ ▼ ▼
┌──────────────┐ ┌───────────────┐ ┌──────────────┐
│ CANCELLED │ │ SUSPENDED │ │ EXPIRED │
│ (Annullato) │ │ (Sospeso) │ │ (Scaduto) │
└──────────────┘ └───────┬───────┘ └──────────────┘
│ │ │
│ riattiva() │ riattiva() │
└──────────────────────────────┼───────────────────┘
│
▼
┌──────────────┐
│ ACTIVE │
│ (Attivo) │
└──────────────┘
州の詳細
- 保留中 (保留中): Stripeでビルドした後の初期状態。サブスクリプションは最初の支払いの確認を待っています
- アクティブ: ユーザーはプランの機能に完全にアクセスできます。プレミアムアクセスを許可する唯一の州
- キャンセル: ユーザーがキャンセルを要求しました。すでに支払われた期間が終了するまでアクセスは有効なままにすることができます
- 一時停止中: 支払いが失敗したため、一時停止が発生しました。プレミアム機能へのアクセスがブロックされている
- 期限切れ (期限切れ): サブスクリプションの有効期限が切れています。新しいサブスクリプションが必要です
public enum StatoAbbonamento {
ACTIVE("Attivo"),
CANCELLED("Annullato"),
EXPIRED("Scaduto"),
SUSPENDED("Sospeso"),
PENDING("In Attesa");
// Solo ACTIVE consente funzionalità premium
public boolean consenteFunzionalitaPremium() {
return this == ACTIVE;
}
// PENDING, SUSPENDED, EXPIRED richiedono pagamento
public boolean richiedePagamento() {
return this == PENDING || this == SUSPENDED || this == EXPIRED;
}
// ACTIVE e CANCELLED sono potenzialmente attivi
public boolean ePotenzialmenteAttivo() {
return this == ACTIVE || this == CANCELLED;
}
// CANCELLED, SUSPENDED, EXPIRED possono essere riattivati
public boolean puoEssereRiattivato() {
return this == CANCELLED || this == SUSPENDED || this == EXPIRED;
}
}
支払い失敗の管理
失敗した支払いの管理は、システムの最も重要な側面の 1 つです サブスクリプションの数。ユーザーのカードの有効期限が切れているか、資金が不足しているか、問題が発生している可能性があります 銀行に一時的に。システムは次の戦略を実行します。 3 回の自動再試行 サブスクリプションを一時停止する前に。
public class Abbonamento {
private int tentativiPagamentoFalliti = 0;
private String ultimoErrorePagamento;
/**
* Registra un pagamento fallito.
* Dopo 3 tentativi falliti, l'abbonamento viene sospeso.
*/
public void registraPagamentoFallito(String errore) {
this.ultimoErrorePagamento = errore;
this.tentativiPagamentoFalliti++;
if (this.tentativiPagamentoFalliti >= 3) {
this.stato = StatoAbbonamento.SUSPENDED;
}
}
/**
* Registra un pagamento riuscito.
* Resetta il contatore e riattiva se sospeso.
*/
public void registraPagamentoRiuscito() {
this.ultimoPagamentoIl = Instant.now();
this.ultimoErrorePagamento = null;
this.tentativiPagamentoFalliti = 0;
if (this.stato == StatoAbbonamento.SUSPENDED
|| this.stato == StatoAbbonamento.PENDING) {
this.stato = StatoAbbonamento.ACTIVE;
}
}
}
督促戦略(債権回収)
督促戦略は、Stripe 側とアプリケーション側の両方で構成されます。ストライプ スマート再試行ポリシーに従って支払いを自動的に再試行しますが、バックエンドは Webhook 経由で受信した失敗した試行をすべて追跡します。 3回連続の失敗で、 一時停止は自動的に行われ、ユーザーには電子メールで通知されます。支払いが成功した いつでもカウンタを完全にリセットして、サブスクリプションを再アクティブ化できます。
ファクトリメソッド: 正しい方法でサブスクリプションを作成する
クラス Abbonamento を実装します ファクトリーメソッドパターン のために
各サブスクリプション タイプが正しい初期状態で作成されていることを確認してください。これ
このアプローチにより、一貫性のない状態でサブスクリプションが作成される可能性が排除され、一元化されます。
初期化ロジック。
createWithStripe() — 標準サブスクリプション
Stripe 経由で有料サブスクリプションを作成する主な方法。サブスクリプションが始まります
状態で PENDING そしてなる ACTIVE 支払い確認後のみ
Webhook経由で受信しました。
public static Abbonamento creaConStripe(
User utente,
String idClienteStripe,
String idAbbonamentoStripe,
String idPrezzoStripe,
TipoPiano tipoPiano,
CicloPagamento cicloFatturazione,
BigDecimal importo) {
return Abbonamento.builder()
.utente(utente)
.idClienteStripe(idClienteStripe)
.idAbbonamentoStripe(idAbbonamentoStripe)
.idPrezzoStripe(idPrezzoStripe)
.tipoPiano(tipoPiano)
.cicloFatturazione(cicloFatturazione)
.importo(importo)
.valuta("EUR")
.stato(StatoAbbonamento.PENDING) // Attesa conferma pagamento
.dataInizio(Instant.now())
.tentativiPagamentoFalliti(0)
.build();
}
creaGratuito() — 無料プラン
計画については PRIVATE_FREE、Stripe との対話は必要ありません。
サブスクリプションは状態で直接作成されます ACTIVE 金額ゼロで。
public static Abbonamento creaGratuito(User utente) {
return Abbonamento.builder()
.utente(utente)
.tipoPiano(TipoPiano.PRIVATE_FREE)
.cicloFatturazione(CicloPagamento.MENSILE)
.importo(BigDecimal.ZERO)
.valuta("EUR")
.stato(StatoAbbonamento.ACTIVE) // Attivo immediatamente
.dataInizio(Instant.now())
.tentativiPagamentoFalliti(0)
.build();
}
createBypass() — 有効期限のあるプロモーション コード
ユーザーが次のタイプのプロモーション コードを適用したとき BYPASS_PAGAMENTO、取得します
料金を支払うことなくプレミアム プランにアクセスできますが、それに基づいて正確な期限が計算されます。
コードで設定された期間。
public static Abbonamento creaBypass(
User utente,
TipoPiano tipoPiano,
CodicePromozionale codicePromozionale,
int durataMesi) {
return Abbonamento.builder()
.utente(utente)
.tipoPiano(tipoPiano)
.cicloFatturazione(CicloPagamento.MENSILE)
.importo(BigDecimal.ZERO)
.valuta("EUR")
.stato(StatoAbbonamento.ACTIVE)
.dataInizio(Instant.now())
.scadenzaBypass(Instant.now().plus(
durataMesi * 30L, ChronoUnit.DAYS))
.codicePromozionale(codicePromozionale)
.tentativiPagamentoFalliti(0)
.build();
}
createPermanentBypass() — 生涯アクセス
特別なコラボレーション、パートナー、従業員のために、
有効期限のない永久サブスクリプション。そこには scadenzaBypass に設定
null サブスクリプションに有効期限がないことを明示的に示します。
public static Abbonamento creaBypassPermanente(
User utente,
TipoPiano tipoPiano,
CodicePromozionale codicePromozionale) {
return Abbonamento.builder()
.utente(utente)
.tipoPiano(tipoPiano)
.cicloFatturazione(CicloPagamento.MENSILE)
.importo(BigDecimal.ZERO)
.valuta("EUR")
.stato(StatoAbbonamento.ACTIVE)
.dataInizio(Instant.now())
.scadenzaBypass(null) // Null = permanente, mai scade
.codicePromozionale(codicePromozionale)
.tentativiPagamentoFalliti(0)
.build();
}
// Metodi di verifica
public boolean isAbbonamentoBypass() {
return scadenzaBypass != null;
}
public boolean isPermanente() {
return codicePromozionale != null
&& scadenzaBypass == null
&& importo.compareTo(BigDecimal.ZERO) == 0;
}
プロモーションコード制度
プロモーションコードシステムは、 イベントをプレイする 割引を伴うマーケティング キャンペーンなど、さまざまな商業シナリオをサポートするように設計されています。 割合または固定、戦略的パートナーへの支払いを完全に回避するまで。すべてのコード プロモーションは、検証ルール、使用制限、および完全な追跡を備えたエンティティです。
プロモーションコードの種類
- DISCOUNT_PERCENTAGE: Stripe プロモーション コードを介してパーセント割引 (例: 30%) を適用します。が必要です
idPromotionCodeStripe設定済み - 固定割引: Stripe プロモーション コードを使用して、ユーロ単位の固定割引 (例: 10 ユーロ) を適用します。これにはStripeでの設定も必要です
- バイパス_パガメント: 期間を限定して完全な支払いバイパスを実現します。 Stripe の支払いを必要とせずに有効期限を設定できるプレミアム プランをアクティブ化します
- PERMANENT_BYPASS: 特別なコラボレーション、パートナー、従業員のための生涯バイパス。サブスクリプションには有効期限がありません
public enum TipoCodicePromozionale {
BYPASS_PAGAMENTO,
BYPASS_PERMANENTE,
SCONTO_PERCENTUALE,
SCONTO_FISSO;
// Solo SCONTO_* richiedono configurazione Stripe
public boolean richiedeStripe() {
return this == SCONTO_PERCENTUALE || this == SCONTO_FISSO;
}
// BYPASS_* saltano completamente il pagamento
public boolean bypassaPagamento() {
return this == BYPASS_PAGAMENTO || this == BYPASS_PERMANENTE;
}
public boolean isPermanente() {
return this == BYPASS_PERMANENTE;
}
}
コードの検証
適用される前に、各プロモーション コードは一連のチェックを受けます。 有効期間内に有効であり、利用可能な用途があり、互換性がある必要があります。 ユーザーが選択したプラン。
@Entity
@Table(name = "codici_promozionali")
public class CodicePromozionale {
private String codice; // "WELCOME50", "PARTNER2024"
private TipoCodicePromozionale tipo;
private BigDecimal valore; // Sconto % o fisso (null per bypass)
private TipoPiano tipoPianoApplicabile; // Null = tutti i piani
private Integer durataMesiBypass; // Solo per BYPASS_PAGAMENTO
private Integer maxUtilizzi; // Null = illimitati
private int utilizziCorrenti;
private boolean attivo;
public boolean isValido() {
if (!attivo) return false;
Instant now = Instant.now();
if (validoDa != null && now.isBefore(validoDa)) return false;
if (validoFino != null && now.isAfter(validoFino)) return false;
if (maxUtilizzi != null && utilizziCorrenti >= maxUtilizzi)
return false;
return true;
}
public boolean isApplicabileAPiano(TipoPiano piano) {
if (tipoPianoApplicabile == null) return true;
return tipoPianoApplicabile == piano;
}
}
使用状況の追跡
プロモーション コードを使用するたびにエンティティに記録されます。
UtilizzoCodicePromozionale、誰が、いつ、どこでコードを使用したかを追跡します。
コンテキスト (登録またはチェックアウト)、およびどのサブスクリプションが作成または変更されたか。
@Entity
@Table(name = "utilizzi_codici_promozionali")
public class UtilizzoCodicePromozionale {
private CodicePromozionale codicePromozionale;
private User utente;
private Abbonamento abbonamento;
private Instant utilizzatoIl;
private String contesto; // "REGISTRAZIONE" o "CHECKOUT"
// Factory methods per i due contesti
public static UtilizzoCodicePromozionale perRegistrazione(
CodicePromozionale codice, User utente, Abbonamento abb) {
return UtilizzoCodicePromozionale.builder()
.codicePromozionale(codice)
.utente(utente)
.abbonamento(abb)
.utilizzatoIl(Instant.now())
.contesto("REGISTRAZIONE")
.build();
}
public static UtilizzoCodicePromozionale perchèckout(
CodicePromozionale codice, User utente, Abbonamento abb) {
// ... stesso pattern con contesto "CHECKOUT"
}
}
Stripe Webhook: リアルタイム更新
Il StripeWebhookController Stripe からプッシュ イベントを受信するエンドポイントです
サブスクリプションまたは支払いのステータスが変更されたとき。このアプローチ
イベント駆動型 ローカルデータベースが常に同期していることを保証します
定期的なポーリングを必要とせずに、Stripe での支払いの実際のステータスを確認できます。
管理イベント
customer.subscription.created— 新しいサブスクリプションが作成されました: サブスクリプションをローカル データベースに同期しますcustomer.subscription.updated— 更新されたサブスクリプション: プラン、ステータス、または今後の請求の変更を反映します。customer.subscription.deleted— サブスクリプションがキャンセルされました: サブスクリプションをキャンセルとしてマークしますinvoice.paid— 支払い済みの請求書: 成功した支払いを記録し、一時停止された場合は再有効化しますinvoice.payment_failed— 支払い失敗: 失敗カウンターが増加し、3 回の試行後に中断されます
@RestController
@RequestMapping("/api/v1/webhooks/stripe")
public class StripeWebhookController {
@PostMapping
public ResponseEntity<String> handleWebhook(
@RequestBody String payload,
@RequestHeader("Stripe-Signature") String stripeSignature) {
// 1. Verifica firma con webhook secret
Event event = Webhook.constructEvent(
payload, stripeSignature, webhookSecret);
// 2. Dispatch in base al tipo di evento
switch (event.getType()) {
case "customer.subscription.created"
-> handleSubscriptionCreated(event);
case "customer.subscription.updated"
-> handleSubscriptionUpdated(event);
case "customer.subscription.deleted"
-> handleSubscriptionDeleted(event);
case "invoice.paid"
-> handleInvoicePaid(event);
case "invoice.payment_failed"
-> handleInvoicePaymentFailed(event);
}
// 3. Ritorna sempre 200 per evitare retry di Stripe
return ResponseEntity.ok("Evento ricevuto");
}
}
Webhook のセキュリティ
受信した各 Webhook は署名によって検証されます Stripe-Signature con il
webhookSecret 設定されています。この検証により、イベントの発生元が保証されます。
実際には、Stripe からのものであり、状態を操作しようとする攻撃者からのものではありません。
サブスクリプション。開発モードでは、署名検証を行うことができます。
Stripe CLI でのテストを容易にするために無効にされています。
チェックアウトとカスタマーポータル
チェックアウトフローでは、 ストライプチェックアウト、ホストされたチェックアウト ページ カードデータ収集、コンプライアンスを自動管理するStripeから PCI-DSS、3D セキュア、通貨換算。バックエンドはチェックアウト セッションを作成し、 URLをフロントエンドに返します。
public Session creaCheckoutSession(
String customerId,
String priceId,
String successUrl,
String cancelUrl,
String promotionCodeId) throws StripeException {
var paramsBuilder = SessionCreateParams.builder()
.setCustomer(customerId)
.setMode(SessionCreateParams.Mode.SUBSCRIPTION)
.addLineItem(SessionCreateParams.LineItem.builder()
.setPrice(priceId)
.setQuantity(1L)
.build())
.setSuccessUrl(successUrl + "?session_id={CHECKOUT_SESSION_ID}")
.setCancelUrl(cancelUrl);
if (promotionCodeId != null) {
// Codice promo predefinito
paramsBuilder.addDiscount(
SessionCreateParams.Discount.builder()
.setPromotionCode(promotionCodeId)
.build());
} else {
// Permetti inserimento manuale
paramsBuilder.setAllowPromotionCodes(true);
}
return Session.create(paramsBuilder.build());
}
購入後の管理については、 Stripe カスタマーポータル ユーザーができるようにします 支払い方法の更新、請求書の表示、プランの切り替え、またはキャンセル サブスクリプションでは、バックエンドでこれらの機能を実装する必要はありません。
イベント購入
サブスクリプション システムに加えて、プラットフォームは次のサポートを提供します。 単品購入
エンティティを介して特定のイベントに関連付けられる AcquistoEvento。これ
この機能により、主催者はイベントに必要なアイテムを追跡できます。
イベントの完了ステータスを管理します。
@Entity
@Table(name = "acquisto_evento")
public class AcquistoEvento {
private Evento evento;
private String nome;
private Integer quantità;
private String note;
private Boolean completato;
private User completatoDa;
private Instant completatoIl;
private User creatoDa;
public static AcquistoEvento crea(
Evento evento, String nome, Integer quantità,
String note, User creatoDa) {
// Validazioni e creazione
}
public void completaAcquisto(User utente) {
this.completato = true;
this.completatoDa = utente;
this.completatoIl = Instant.now();
}
public void toggleCompletamento(User utente) {
if (this.completato) annullaCompletamento();
else completaAcquisto(utente);
}
}
資金調達とクラウドファンディング イベント
イベントをプレイする
のシステムが含まれています イベント資金調達 これにより、寄付者とスポンサーが可能になります
イベントの実現に金銭的に貢献すること。実体 Finanziamento
さまざまなタイプの資金提供をモデル化し、それぞれに確認フローと
完全な追跡。
融資の種類
- 寄付: 見返りを期待しない自発的な寄付
- スポンサーシップ: イベント中のブランド認知度による企業貢献
- 資金調達: 可能な返済条件付きのストラクチャード投資
- 貢献: 一般的な経済参加(例:分割された参加株式)
@Entity
@Table(name = "finanziamenti")
public class Finanziamento {
private Long eventoId;
private Long donatoreId;
private String donatoreNome;
private String donatoreEmail;
private String titolo;
private TipoFinanziamento tipo;
@Embedded
private Money importo; // Value Object immutabile
private LocalDate dataFinanziamento;
private Boolean confermato;
public static Finanziamento crea(
Long eventoId, String titolo, Money importo,
TipoFinanziamento tipo, String donatoreNome,
Long registratoDaId, LocalDate dataFinanziamento) {
// Validazioni rigorose: importo positivo,
// titolo e donatore obbligatori
}
public void conferma() {
this.confermato = true;
this.confermatoIl = Instant.now();
this.dataRicezione = LocalDate.now();
}
}
// Il Value Object Money garantisce operazioni
// monetarie sicure con valuta esplicita
Money donazione = Money.euro(150.00);
Money sponsor = Money.of(new BigDecimal("500.00"), "EUR");
Money totale = donazione.add(sponsor); // EUR 650.00
支払いアーキテクチャの概要
- Stripe の完全な統合: 顧客、サブスクリプション、チェックアウト、カスタマー ポータル、Webhook
- 個人、ビジネス、団体向けに編成された 11 の料金プラン
- 5 つの状態と制御された遷移を備えたステート マシン
- 支払い失敗による一時停止までに 3 回の自動再試行
- 一貫してサブスクリプションを作成するための 4 つのファクトリ メソッド
- 使用量追跡機能付きの 4 種類のプロモーション コード
- リアルタイム同期のためのイベント駆動型 Webhook
- 4種類の拠出とマニュアル確認による融資制度
- タイプセーフな金融取引のためのValue Object Money
支払いソース コードは次の場所で入手できます。 GitHub。 次の記事では、プラットフォームのもう 1 つの基本的な側面について詳しく説明します。 のアーキテクチャへの旅を続けます イベントをプレイする.







