resend.emails.send를 SendGrid, Postmark, Amazon SES 또는 자체 SMTP relay를 호출하는 코드로 바꾸면 됩니다.- dashboard에서 이메일용 custom credit entitlement를 생성합니다.
- subscription plan과 one-time top-up product에 크레딧을 연결합니다.
- Resend를 통해 이메일을 전송하고 ledger entry와 함께 전송당 크레딧 1개를 차감합니다.
- frontend에서 고객의 실시간 credit balance를 읽습니다.
- Dodo Payments webhooks를 검증하고
credit.balance_low를 처리하여 잔액이 0이 되기 전에 고객에게 알립니다.
What We’re Building
MailKit은 두 가지 product를 판매합니다:- Dodo Payments account. 모든 작업은 test mode에서 수행합니다.
- 무료 Resend account와 API key.
- Node.js 22 이상 및 TypeScript에 대한 기본 지식.
Step 1: 이메일 Credit Entitlement 생성
credit entitlement는 MailKit이 판매하는 단위인 이메일 1회 전송을 정의합니다.
The Credits tab under Products lists all your credit entitlements.
Open the Credits Section
- Dodo Payments dashboard에 로그인합니다.
- 사이드바에서 Products를 클릭합니다.
- Credits tab을 선택합니다.
- Create Credit을 클릭합니다.
Configure the Credit Unit
Email CreditsCredit Type: Custom UnitUnit Name: emailDefine Precision: 0. 이메일은 정수 단위이므로 잔액에 소수점이 필요하지 않습니다.Credit Expiry: 30 days. 사용하지 않은 크레딧은 발급 후 30일이 지나면 만료됩니다.Leave the Other Defaults
Save and Copy the Credit ID
cde_로 시작하는 ID를 복사합니다. backend는 balance 조회와 ledger entry에 이 ID를 사용합니다.Email Credits entitlement가 준비되었습니다. 다음으로 고객에게 이를 부여하는 product를 생성합니다.Step 2: Plan 및 Top-Up Pack 생성
동일한Email Credits entitlement를 연결하는 두 product를 생성합니다. 하나는 각 billing cycle마다 이메일 5,000개를 제공하는 Subscription plan이고, 다른 하나는 필요할 때 이메일 5,000개를 추가하는 One Time top-up입니다.
MailKit Plan ($19/month, 이메일 5,000개)
Create the Subscription
- Products로 이동하고 Add Product를 클릭합니다.
- product details를 입력합니다:
MailKit PlanDescription: 5,000 transactional emails per month.- Pricing Type에서 Subscription을 선택합니다.
- recurring price를 설정합니다:
19.00Repeat payment every: 1 monthCurrency: USDAttach the Email Credit Entitlement
Email CreditsCredits issued per billing cycle: 5000Low Balance Threshold (%): 20. 잔액이 cycle당 발급된 크레딧의 20%인 이메일 1,000개 미만으로 떨어지면 Dodo Payments가 credit.balance_low를 전송합니다.Import Default Credit Settings: 켭니다. 그러면 product가 Step 1의 30일 만료 설정을 사용합니다.크레딧을 product에 추가한 다음 product를 저장합니다. pdt_로 시작하는 product ID를 복사합니다.Top-Up Pack ($9 One-Time, 이메일 5,000개)
Create a One-Time Product
- Products로 이동하고 Add Product를 클릭합니다.
- product details를 입력합니다:
Email Top-Up PackDescription: Add 5,000 emails to your MailKit balance.- Pricing Type에서 One Time을 선택합니다.
- price를 설정합니다:
9.00Currency: USDAttach the Credit Grant
- Select credits:
Email Credits - No of credits issued:
5000
Step 3: Backend 설정
checkout을 생성하고, 이메일을 전송하고, balance를 읽고, webhook을 수신하는 Express server를 구축합니다.Initialize the Project
package.json에 dev script를 추가합니다:Configure Environment Variables
.env를 생성합니다:DODO_PAYMENTS_WEBHOOK_KEY를 입력합니다. resend.com/api-keys에서 Resend API key를 생성합니다.Build the Server
server.ts를 생성합니다. server는 subscribe checkout, top-up checkout, balance read, send, webhook receiver의 다섯 route를 제공합니다.Add a Demo UI
public/index.html를 생성합니다. 간단한 form에서 각 route를 호출하므로 browser에서 flow를 테스트할 수 있습니다:Step 4: Webhook Endpoint 연결
credit.balance_low event를 사용하면 고객의 크레딧이 소진되기 전에 알릴 수 있습니다. 이 event가 없으면 고객은 이메일 전송이 실패한 뒤에야 문제를 알게 됩니다.
Expose Your Local Server
https://1234abcd.ngrok-free.app.Register the Endpoint in Dodo Payments
- Developer → Webhooks로 이동하고 Add endpoint를 클릭합니다.
- 자체 tunnel host를 사용하여 URL
https://1234abcd.ngrok-free.app/webhooks/dodo를 입력합니다. credit.added,credit.balance_low,credit.rolled_overevent를 선택합니다.- Create endpoint를 클릭합니다.
- endpoint의 Overview tab에서 signing secret을 복사하여
.env의DODO_PAYMENTS_WEBHOOK_KEY로 입력합니다. - server를 재시작합니다.
Step 5: 전체 Flow 테스트
Start the Server
MailKit running on http://localhost:3000를 log에 기록합니다. browser에서 해당 URL을 엽니다.Subscribe a Test Customer
- section 1에서 test email address와 name을 입력한 다음 Get checkout link를 클릭합니다.
- link를 열고 test card로 checkout을 완료합니다.
- dashboard에서 Customers로 이동하고
cus_로 시작하는 새 customer ID를 복사합니다.
Send an Email
- section 3에 customer ID를 붙여 넣습니다.
- To를 모든 message를 수신하는 Resend test address인
delivered@resend.dev로 둡니다. - Send를 클릭합니다.
Trigger the Low-Balance Webhook
- Customers에서 고객을 열고 Credits tab을 선택한 다음 Email Credits를 선택합니다.
- Apply Credit/Debit을 클릭하고 Debit을 선택한 뒤
4000를 입력합니다. 이제 balance는 정확히 1,000이며 아직 threshold 미만은 아닙니다. - demo에서 이메일을 하나 더 전송합니다. balance가 999로 떨어집니다.
Buy a Top-Up Pack
- section 4에 customer ID를 붙여 넣습니다.
- Buy 5,000 emails를 클릭하고 test checkout을 완료합니다.
- balance를 새로 고칩니다. 5,000 증가합니다.
transaction_type: "credit_added"와 함께 credit.added event를 전송합니다. 이 event에 연결된 grant에는 source_type: one_time가 있으며 List Customer Grants API로 이를 조회할 수 있습니다. Top-up credits는 subscription credits에 추가됩니다. debit은 먼저 만료되는 grant에서 차감되며, 두 grant가 동시에 만료되면 더 오래된 grant에서 차감됩니다.Test the Hard Stop
402와 함께 응답합니다:402는 application의 enforcement입니다. Dodo Payments balance API를 source of truth로 사용하고 client에 balance를 cache하지 마세요.문제 해결
Webhook signature verification fails (401)
Webhook signature verification fails (401)
express.json()는 body를 parsed object로 바꾸므로 verification이 실패합니다. /webhooks/dodo를 express.raw({ type: 'application/json' })와 함께 app.use(express.json()) line 위에 등록합니다. 그런 다음 DODO_PAYMENTS_WEBHOOK_KEY가 endpoint의 Overview tab에 있는 signing secret과 일치하는지 확인합니다.Balance is 0, customer not found, or credits don't deduct
Balance is 0, customer not found, or credits don't deduct
- 고객이 checkout을 완료했는지 확인합니다. 크레딧은 checkout session이 생성될 때가 아니라 payment가 성공할 때 발급됩니다.
.env의CREDIT_ENTITLEMENT_ID가 product에 연결된 credit과 일치하는지 확인합니다. balance 및 ledger call은 이 ID를 사용하므로 불일치하면 다른 credit을 조회하거나 차감합니다.- 전달하는
customer_id가 자체 database의 ID가 아니라 Dodo Payments customer ID인지 확인합니다. 이 ID는cus_로 시작합니다.
Resend rejects the recipient
Resend rejects the recipient
onboarding@resend.dev는 Resend account의 email address 또는 delivered@resend.dev로만 전송합니다. 다른 사람에게 보내려면 domain을 verify하고 해당 domain의 from address를 사용합니다.구축한 항목
One Reusable Credit Unit
Email Credits입니다.Subscription with Prepaid Allowance
Top-Up Pack
Direct Ledger Debits
createLedgerEntry call을 한 번 수행하며, meter나 aggregation delay가 필요하지 않습니다. Resend message ID를 idempotency key로 사용하면 동일한 전송에 대한 두 번째 debit을 방지할 수 있습니다.