Stripe를 통한 결제 및 구독
다음과 같은 SaaS 플랫폼에서는 이벤트를 플레이하세요, 결제 관리는 단순한 액세서리 모듈이 아닙니다. 결제 관리의 핵심입니다. 비즈니스 모델. 각 사용자는 요금제를 선택하고 구독하고, 자동 청구서를 받고, 결제 수단을 완벽하게 관리하세요 자율성. 이를 달성하기 위해 플랫폼은 다음과 통합됩니다. 줄무늬, SaaS 세계에서 가장 많이 사용되는 결제 제공업체 결제, 구독, 고객 포털 및 웹훅을 위한 API입니다.
이 기사에서는 결제 시스템의 아키텍처를 자세히 분석합니다. 관리를 통해 구독 상태 머신에 대한 관세 계획 정의 결제 실패, 프로모션 코드 및 이벤트 파이낸싱 시스템.
이 기사에서 찾을 수 있는 내용
- Stripe과의 통합 아키텍처(고객, 구독, 가격 ID)
- 카테고리(개인, 기업, 협회)별로 정리된 구독 요금제
- 월간 및 연간 청구 주기
- 구독 상태 머신(PENDING → ACTIVE → CANCELLED/SUSPENDED/EXPIRED)
- 3회 자동 재시도를 통한 결제 실패 관리
- 구독 생성을 위한 팩토리 메소드(Stripe, Free, Bypass)
- 결제 우회 및 영구 우회가 가능한 프로모션 코드 시스템
- 실시간 업데이트를 위한 스트라이프 웹훅 관리
- 이벤트 구매 및 파이낸싱/크라우드펀딩 시스템
스트라이프 통합 아키텍처
Stripe과의 통합은 정확한 아키텍처 패턴을 따릅니다. 오케스트레이터 비밀 키를 노출하지 않고 사용자와 Stripe API 사이 프론트엔드에. 통신 흐름은 다음 세 가지 핵심 Stripe 개념을 기반으로 합니다. 애플리케이션 도메인에 매핑됩니다.
통합의 세 가지 기둥
- 고객: 플랫폼에 등록된 각 사용자는 개체와 연결됩니다.
Customer스트라이프에서 다음으로 식별됨idClienteStripe. 이를 통해 Stripe는 결제 방법 및 거래 내역을 추적할 수 있습니다. - 신청: 사용자가 유료 요금제에 가입하면
Subscription스트라이프에서 다음으로 식별됨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과 Price ID 간의 양방향 매핑을 관리합니다.
결제 세션 생성과 해결을 모두 허용하는 내부 가격 계획
웹훅에서 시작하는 계획과 반대입니다.
@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);
}
}
구독 계획
이벤트를 플레이하세요
세 가지 계정 범주로 구분된 가격 책정 구조를 제공하며 각 범주에는 계획이 포함됩니다.
점차적으로 기능이 풍부해졌습니다. 열거형 TipoPiano 정의하다
가격, 한도 및 계정 유형과의 호환성을 갖춘 사용 가능한 모든 계획.
프라이빗 플랜(개인)
- PRIVATE_FREE: 기본 한도(3개 이벤트, 50명 참여)가 포함된 무료 플랜입니다. 플랫폼을 시험해보고 싶은 사람들에게 이상적입니다.
- PRIVATE_STARTER: €4.99/월 또는 €49.90/년 — 10개 이벤트, 100명 참가자
- PRIVATE_PRO: €9.99/월 또는 €99.90/년 — 무제한 이벤트, 300명의 참가자, AI 분석
- PRIVATE_PREMIUM: €19.99/월 또는 €199.90/년 — 무제한 이벤트, 1000명의 참가자, 모든 기능
사업계획(회사)
- BUSINESS_STARTER: €29/월 또는 €290/년 — 무제한 이벤트, 참가자 200명, 팀 5명
- 비즈니스_전문: €79/월 또는 €790/년 — 무제한 이벤트, 참가자 500명, 팀원 15명
- 비즈니스_기업: €199/월 또는 €1990/년 — 무제한 이벤트, 무제한 참석자, 우선 지원
협회 계획(비영리)
- 무제한 연결: €24/년 — 무제한 이벤트 및 참가자가 포함된 단일 플랜이 모두 포함되어 있습니다. 제3섹터를 지원하기 위한 윤리적 선택
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%)입니다.
청구주기
시스템은 열거형으로 정의된 두 가지 청구 주기를 지원합니다. 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 다섯 가지 가능한 값으로 이 상태 머신을 모델링합니다.
각 전환에는 허용되는 전환과 전환이 어떤 기능인지에 대한 정확한 규칙이 있습니다.
접근 가능.
┌──────────────────────────────────────────────────┐
│ │
│ 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;
}
}
결제 실패 관리
실패한 결제를 관리하는 것은 시스템의 가장 중요한 측면 중 하나입니다. 구독의. 사용자에게 카드가 만료되었거나 자금이 부족하거나 문제가 있을 수 있습니다. 은행에 임시로. 시스템은 다음과 같은 전략을 구현합니다. 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 측과 애플리케이션 측 모두에서 구성됩니다. 스트라이프 Smart Retries 정책에 따라 자동으로 결제를 재시도하며, 백엔드는 웹훅을 통해 수신된 모든 실패한 시도를 추적합니다. 3년 연속 실패, 정지는 자동으로 이루어지며 사용자에게 이메일을 통해 통보됩니다. 성공적인 결제 언제든지 카운터를 완전히 재설정하고 구독을 다시 활성화하세요.
팩토리 메소드: 올바른 방법으로 구독 생성
수업 Abbonamento 구현 팩토리 메소드 패턴 에 대한
각 구독 유형이 올바른 초기 상태로 생성되었는지 확인하세요. 이
접근 방식은 일관되지 않은 상태로 구독을 생성할 가능성을 제거하고 중앙 집중화합니다.
초기화 논리.
createWithStripe() — 표준 구독
Stripe을 통해 유료 구독을 생성하는 기본 방법입니다. 구독이 시작됩니다
상태 PENDING 그리고 된다 ACTIVE 결제 확인 후에만
웹훅을 통해 수신되었습니다.
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 금액이 0입니다.
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구성된 - FIXED_DISCOUNT: Stripe 프로모션 코드를 통해 EUR(예: €10)로 고정 할인을 적용하세요. 이를 위해서는 Stripe에 대한 구성도 필요합니다.
- BYPASS_PAGAMENTO: 제한된 기간 동안 결제 우회를 완료하세요. 구성 가능한 만료로 Stripe 결제 없이 프리미엄 플랜을 활성화하세요
- 영구_바이패스: 특별한 협력, 파트너 및 직원을 위한 평생 우회. 구독이 만료되지 않습니다
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 Webhooks: 실시간 업데이트
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");
}
}
웹훅 보안
수신된 각 웹훅은 서명으로 확인됩니다. Stripe-Signature 와 함께
webhookSecret 구성되었습니다. 이 확인은 이벤트가 다음에서 발생함을 보장합니다.
실제로는 Stripe에서 온 것이지 상태를 조작하려는 공격자가 보낸 것이 아닙니다.
구독. 개발 모드에서는 서명 확인이 가능합니다.
Stripe CLI를 사용한 테스트를 용이하게 하기 위해 비활성화되었습니다.
결제 및 고객 포털
결제 흐름은 다음을 사용합니다. 스트라이프 체크아웃, 호스팅된 결제 페이지 카드 데이터 수집, 규정 준수를 자동으로 관리하는 Stripe에서 PCI-DSS, 3D Secure 및 통화 변환. 백엔드는 결제 세션을 생성하고 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());
}
구매 후 관리를 위해, 스트라이프 고객 포털 사용자는 다음을 수행할 수 있습니다. 결제 방법 업데이트, 송장 보기, 요금제 전환 또는 취소 백엔드에서 이러한 기능을 구현하지 않고도 구독이 가능합니다.
이벤트 구매
구독 시스템 외에도 플랫폼은 다음을 지원합니다. 단일 구매
엔터티를 통해 특정 이벤트와 연결됨 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 통합: 고객, 구독, 체크아웃, 고객 포털, 웹후크
- 민간, 기업 및 협회를 위해 구성된 11가지 관세 계획
- 5가지 상태와 제어된 전환을 갖춘 상태 머신
- 결제 실패로 인해 정지되기 전 자동 재시도 3회
- 지속적으로 구독을 생성하는 4가지 팩토리 메소드
- 사용량 추적 기능이 있는 4가지 유형의 프로모션 코드
- 실시간 동기화를 위한 이벤트 기반 웹후크
- 4가지 유형의 기부와 수동 확인을 통한 자금조달 시스템
- 유형이 안전한 화폐 거래를 위한 Value Object Money
결제 소스 코드는 다음에서 확인할 수 있습니다. GitHub. 다음 기사에서는 플랫폼의 또 다른 기본 측면을 살펴보겠습니다. 건축으로의 여행을 계속하다 이벤트를 플레이하세요.







