Adaptive Payment Plan
Payment plans allow merchants to define complex rules for when and how payments should occur. Unlike traditional subscription systems with rigid timing and amount structures, Blue payment plans can respond to any event, trigger based on any condition, and create sophisticated payment flows that would be difficult or impossible to implement with standard payment APIs.
Beyond Simple Subscriptions
While payment processors like Stripe offer subscription capabilities, Blue payment plans extend far beyond simple recurring billing:
- Event-driven payments - trigger charges based on any verifiable event
- Conditional logic - only charge when specific conditions are met
- Variable amounts - calculate payment amounts dynamically
- Multi-party verification - ensure all parties agree before payments move
- Transparent rules - make payment logic visible and verifiable to all participants
The Payment Plan Type
Here's the foundational Payment Plan type:
name: Payment Plan
description: A dynamic document that manages when and how payments are triggered
The Payment Plan is a generic type that can be extended and customized for specific use cases. Payment processors can listen to events like Process Payment
or Process Debit
, which contain the details of a Card Payment to process.
Example: Netflix-Style Subscription
This example shows a simple monthly subscription similar to what Netflix might use:
name: Premium Video Subscription
type: Payment Plan
currency: USD
token tok_1234
subscriptionDetails:
planName: "Premium"
price: 19.99
contracts:
merchant:
type: MyOS Account
email: streaming-service@example.com
customer:
type: MyOS Account
email: subscriber@example.com
billingScheduleTrigger:
type: MyOS Account
email: monthly+2024-05-01T09:00Z@datetime.myos.blue
monthlyBillingTrigger:
type: Sequential Workflow
channel: billingScheduleChannel
steps:
- name: PreparePayment
type: JavaScript Code
code: |
return {
amount: document('/subscriptionDetails/price'),
description: `Monthly charge for ${document('/subscriptionDetails/planName')} plan`
};
- name: TriggerPayment
type: Trigger Event
event:
type: Process Payment
payment:
amount: ${steps.PreparePayment.amount}
currency: ${document('/currency')}
description: ${steps.PreparePayment.description}
token: ${document('/paymentMethod/token')}
This example maps directly to subscription capabilities already offered by payment processors, but with additional transparency. Both the merchant and customer can verify exactly when payments will be triggered and for what amount.
Example: Conditional Payment Based on Sports Outcome
Here's how you could create a payment that only processes if the Lakers win their next game:
name: Lakers Victory Payment
type: Payment Plan
currency: USD
paymentMethod:
token: tok_1234
type: card
betDetails:
amount: 100.00
team: "Los Angeles Lakers"
opponent: "Boston Celtics"
gameDate: "2023-10-15"
status: "pending"
contracts:
bettor:
type: MyOS Account
email: fan@example.com
bookie:
type: MyOS Account
email: sportsbook@example.com
sportsResultsChannel:
type: MyOS Account
email: official-nba-results@example.com
gameResultProcessor:
type: Sequential Workflow
channel: sportsResultsChannel
event:
type: Game Result
steps:
- name: VerifyGame
type: JavaScript Code
code: |
// Verify this is the game we're interested in
const isTargetGame =
event.homeTeam === document('/betDetails/team') &&
event.awayTeam === document('/betDetails/opponent') ||
event.awayTeam === document('/betDetails/team') &&
event.homeTeam === document('/betDetails/opponent');
if (!isTargetGame) {
return { relevantGame: false };
}
// Check if Lakers won
const lakersWon =
(event.homeTeam === document('/betDetails/team') && event.homeScore > event.awayScore) ||
(event.awayTeam === document('/betDetails/team') && event.awayScore > event.homeScore);
return {
relevantGame: true,
lakersWon: lakersWon
};
- name: ProcessGameResult
type: Update Document
condition: ${steps.VerifyGame.relevantGame === true}
changeset:
- op: replace
path: /status
val: ${steps.VerifyGame.lakersWon ? "approved" : "rejected"}
- name: TriggerPaymentIfWon
type: Trigger Event
condition: ${steps.VerifyGame.relevantGame === true && steps.VerifyGame.lakersWon === true}
event:
type: Process Payment
payment:
amount: ${document('/betDetails/amount')}
currency: ${document('/currency')}
description: "Payment for Lakers victory over ${document('/betDetails/opponent')}"
token: ${document('/paymentMethod/token')}
Business Value: The customer can be confident that payment will only process if the Lakers actually win. The payment depends on data from an official NBA results channel, not just the merchant's claim. The payment processor enforces these rules, ensuring the merchant can't charge unless the agreed-upon condition is met.
Example: Dynamic Balance Payment Plan
This example shows how a payment plan can accumulate charges based on activity and only process payments when requested by the recipient:
name: AdWords Promotion Payment Plan for Xavier
type: Payment Plan
currency: USD
balance: 0.00
paymentMethod:
token: tok_1234
type: card
contracts:
merchant:
type: MyOS Account
account: alice@example.com
recipient:
type: MyOS Account
account: xavier@example.com
eventsTimeline:
type: MyOS Account
account: ${document('/monitoringTimeline')}
bookingProcessor:
type: Sequential Workflow
channel: eventsTimeline
event:
type: Booking Created
steps:
- name: ExtractAndAddAmount
type: JavaScript Code
code: |
// Extract price amount from booking event
const bookingAmount = event.price?.amount || 0;
return {
amount: bookingAmount,
bookingId: event.id || "unknown"
};
- name: UpdateBalance
type: Update Document
changeset:
- op: replace
path: /balance
val: ${document('/balance') + steps.ExtractAndAddAmount.amount}
cancellationProcessor:
type: Sequential Workflow
channel: eventsTimeline
event:
type: Booking Cancelled
steps:
- name: ExtractAndSubtractAmount
type: JavaScript Code
code: |
// Extract price amount from cancellation event
const bookingAmount = event.price?.amount || 0;
return {
amount: bookingAmount,
bookingId: event.id || "unknown"
};
- name: UpdateBalance
type: Update Document
changeset:
- op: replace
path: /balance
val: ${document('/balance') - steps.ExtractAndSubtractAmount.amount}
requestPayment:
type: Operation
requestPaymentImpl:
type: Sequential Workflow Operation
operation: requestPayment
channel: recipientChannel
steps:
- name: ValidateBalance
type: JavaScript Code
code: |
// Check if there's a balance to pay
const currentBalance = document('/balance');
if (currentBalance <= 0) {
throw new Error("No balance to pay. Current balance: $" + currentBalance);
}
return {
paymentAmount: currentBalance,
notes: event || ""
};
- name: TriggerPayment
type: Trigger Event
event:
type: Process Debit
debit:
amount: ${steps.ValidateBalance.paymentAmount}
currency: ${document('/currency')}
notes: ${steps.ValidateBalance.notes}
token: ${document('/paymentMethod/token')}
- name: UpdatePaymentRecord
type: Update Document
changeset:
- op: replace
path: /balance
val: 0
Business Value: This example demonstrates how real-world business rules can be encoded directly in a payment document. The payment plan responds to booking events, maintaining a running balance that Xavier can request payment for at any time. The payment processor ensures that:
- Only valid booking events affect the balance
- Cancellations properly reduce the balance
- Xavier can only request payment for the actual accumulated balance
- All parties can verify the exact rules being applied
Unlike traditional payment systems where business logic typically lives in custom code, this approach embeds the entire payment flow directly in the document, making it transparent, verifiable, and portable.