Skip to main content

Payment Operations

Payment Operations provides comprehensive payment processing, subscription lifecycle management, and administrative payment retry capabilities within the DashClicks billing system.

API Endpoints Overview

MethodEndpointDescription
POST/v1/admin/billing/subscription/:sub_idRetry payment for past-due subscription
DELETE/v1/admin/billing/subscription/:sub_idCancel subscription
PUT/v1/admin/billing/subscription/:sub_id/resumeResume canceled subscription
POST/v1/admin/billing/subscription/:sub_id/clearClear subscription pending tasks

Service Methods & Business Logic

Payment Retry Operations

retrySub(subId) - Payment retry for past-due subscriptions with Stripe integration

  • Retries payment processing for subscriptions in past-due status with comprehensive error handling
  • Subscription Validation: Verifies subscription exists and is in valid state for retry:
    • Confirms subscription exists in database with past-due status
    • Validates subscription has associated Stripe subscription ID
    • Ensures subscription is eligible for payment retry operation
  • Stripe Payment Processing: Direct Stripe API integration for payment retry:
    • Retrieves latest invoice from subscription data
    • Attempts payment processing through Stripe Invoices API
    • Validates payment success before updating subscription status
  • Status Management: Updates subscription status based on payment outcome:
    • Sets status to 'active' on successful payment processing
    • Maintains 'past_due' status on payment failure with error details
    • Provides detailed error messaging for failed payment attempts
  • Data Aggregation: Returns complete subscription information:
    • Aggregates partner and buyer account information
    • Includes product details and pricing information
    • Provides discount calculations and billing period data
  • MongoDB Collections: _store.subscriptions with partner/buyer/product lookup
  • Returns: Complete subscription object with updated status and payment information

Subscription Cancellation Operations

cancelSub(subId, immediate) - Comprehensive subscription cancellation with Stripe synchronization

  • Cancels subscriptions with flexible timing options and complete billing cleanup
  • Cancellation Modes: Supports multiple cancellation approaches:
    • Period-end cancellation (maintains service until billing period ends)
    • Immediate cancellation (terminates service and billing immediately)
    • Configurable cancellation timing based on administrative requirements
  • Stripe Integration: Complete Stripe subscription lifecycle management:
    • Updates Stripe subscription with period-end cancellation flag
    • Processes immediate cancellation through Stripe API deletion
    • Synchronizes cancellation status between internal and Stripe systems
  • Task Management: Updates subscription administrative status:
    • Sets team_tasks_pending flag for operational team notifications
    • Marks subscription for administrative cleanup and processing
    • Provides workflow integration for post-cancellation operations
  • Invoice Management: Comprehensive invoice cleanup and verification:
    • Voids open invoices to prevent future billing attempts
    • Handles uncollectible invoices with appropriate status updates
    • Processes multiple invoice statuses with pagination support
    • Provides error handling for invoice operations that may fail
  • Error Handling: Robust error management with user-friendly messaging:
    • Translates Stripe error codes to user-understandable messages
    • Handles incomplete_expired status translation to unpaid
    • Provides detailed cancellation failure information
  • MongoDB Collections: _store.subscriptions with complete relationship data
  • Returns: Updated subscription object with cancellation status and timing

Subscription Resume Operations

resumeCancelSub(subId) - Resume previously canceled subscriptions

  • Resumes subscriptions that were scheduled for period-end cancellation
  • Cancellation Reversal: Validates and reverses period-end cancellation:
    • Confirms subscription exists with cancel_at_period_end flag set
    • Validates subscription is eligible for cancellation reversal
    • Ensures subscription is still within active billing period
  • Stripe Synchronization: Updates Stripe subscription settings:
    • Removes cancel_at_period_end flag through Stripe API
    • Restores subscription to normal billing cycle operation
    • Synchronizes subscription status between systems
  • Status Updates: Updates internal subscription status:
    • Removes cancellation flags and pending task indicators
    • Restores subscription to active operational status
    • Updates subscription metadata for continued service
  • Data Consistency: Ensures data consistency across systems:
    • Validates Stripe and internal database synchronization
    • Provides complete subscription information after resume
    • Maintains audit trail of cancellation and resume operations
  • MongoDB Collections: _store.subscriptions with relationship lookup
  • Returns: Complete subscription object with resumed status

Subscription Cleanup Operations

clearSub(subId) - Administrative subscription cleanup for canceled subscriptions

  • Clears administrative flags and completes post-cancellation workflow
  • Status Validation: Validates subscription is eligible for cleanup:
    • Confirms subscription has 'canceled' status
    • Verifies team_tasks_pending flag is set indicating pending work
    • Ensures subscription is ready for administrative cleanup
  • Task Completion: Marks administrative tasks as completed:
    • Removes team_tasks_pending flag to indicate work completion
    • Updates subscription status for operational team workflow
    • Provides administrative closure for canceled subscriptions
  • Workflow Integration: Supports operational team workflow management:
    • Enables tracking of post-cancellation task completion
    • Provides clear status indicators for administrative oversight
    • Supports subscription lifecycle management and reporting
  • Data Integrity: Maintains complete subscription data integrity:
    • Preserves subscription history and cancellation information
    • Maintains relationship data for reporting and analysis
    • Provides complete subscription information after cleanup
  • MongoDB Collections: _store.subscriptions with aggregated data
  • Returns: Updated subscription object with cleared administrative flags

Technical Implementation Details

Payment Retry Implementation

const retrySubscriptionPayment = async subId => {
// Validate subscription exists and is past due
const subscription = await StoreSubscription.findOne({
_id: subId,
status: 'past_due',
});

if (!subscription) {
throw new Error('Subscription not found or not eligible for retry');
}

try {
// Attempt payment through Stripe
const invoice = await stripe.invoices.pay(subscription.latest_invoice);

if (invoice.paid) {
// Update subscription status on successful payment
subscription.status = 'active';
await subscription.save();
} else {
throw new Error('Payment processing failed - invoice not marked as paid');
}
} catch (stripeError) {
// Handle Stripe-specific errors
const errorMessage = `Payment failed: ${stripeError.message}`;
throw new Error(errorMessage);
}

// Return complete subscription data with updated status
const updatedSubscription = await StoreSubscription.aggregate(formatSubscriptionQuery(subId));

return updatedSubscription[0];
};

Subscription Cancellation Implementation

const cancelSubscriptionWithCleanup = async (subId, immediate = false) => {
const subscription = await StoreSubscription.findById(subId);

if (!subscription) {
throw new Error('Subscription not found');
}

try {
let updatedStripeSubscription;

if (!immediate) {
// Period-end cancellation
updatedStripeSubscription = await stripe.subscriptions.update(subscription.stripe_id, {
cancel_at_period_end: true,
});
} else {
// Immediate cancellation
updatedStripeSubscription = await stripe.subscriptions.del(subscription.stripe_id);
}

// Update internal subscription status
delete updatedStripeSubscription.id; // Remove Stripe ID to avoid conflicts
await StoreSubscription.updateOne(
{ _id: subId },
{
team_tasks_pending: true,
...updatedStripeSubscription,
},
);

// Cleanup existing invoices
await cleanupSubscriptionInvoices(subscription.stripe_id);

// Return updated subscription data
const result = await StoreSubscription.aggregate(formatSubscriptionQuery(subId));

return result[0];
} catch (error) {
const errorMessage = error.message.replace('incomplete_expired', 'unpaid');
throw new Error(`Cancellation failed: ${errorMessage}`);
}
};

const cleanupSubscriptionInvoices = async stripeSubscriptionId => {
const invoiceStatuses = ['open', 'uncollectible'];

for (const status of invoiceStatuses) {
try {
let invoiceList = { has_more: true };

while (invoiceList.has_more) {
invoiceList = await stripe.invoices.list({
subscription: stripeSubscriptionId,
status: status,
starting_after: invoiceList.data?.length
? invoiceList.data[invoiceList.data.length - 1].id
: undefined,
});

if (invoiceList.data.length) {
for (const invoice of invoiceList.data) {
await stripe.invoices.voidInvoice(invoice.id);
}
} else {
break;
}
}
} catch (error) {
logger.warn({
message: 'COULD NOT VOID ALL EXISTING INVOICES',
subscriptionId: stripeSubscriptionId,
status,
error: error.message,
});
}
}
};

Subscription Resume Implementation

const resumeCanceledSubscription = async subId => {
const subscription = await StoreSubscription.findOne({
_id: subId,
cancel_at_period_end: true,
});

if (!subscription) {
throw new Error('Cancelled subscription not found or not eligible for resume');
}

try {
// Remove cancellation flag from Stripe
const updatedStripeSubscription = await stripe.subscriptions.update(subscription.stripe_id, {
cancel_at_period_end: false,
});

// Update internal subscription status
delete updatedStripeSubscription.id;
await StoreSubscription.updateOne({ _id: subId }, { ...updatedStripeSubscription });

// Return complete subscription data
const result = await StoreSubscription.aggregate(formatSubscriptionQuery(subId));

return result[0];
} catch (error) {
throw new Error(`Failed to resume cancellation: ${error.message}`);
}
};

API Response Formats

Payment Retry Response

{
"success": true,
"data": {
"partner": {
"name": "DashClicks Agency",
"phone": "+1234567890",
"email": "agency@dashclicks.com",
"account": "partner_account_id"
},
"buyer": {
"name": "Client Business Inc",
"phone": "+1987654321",
"email": "client@business.com",
"account": "buyer_account_id"
},
"product": {
"name": "SEO Management",
"tier": "Professional",
"interval": "month",
"interval_count": 1,
"amount": 29999,
"amount_due": 25499,
"images": ["https://example.com/seo-product.jpg"]
},
"status": "active",
"team_tasks_pending": false,
"cancel_at_period_end": false,
"current_period_start": "2024-10-01T00:00:00.000Z",
"current_period_end": "2024-11-01T00:00:00.000Z",
"subscription_id": {
"stripe": "sub_stripe_id_123",
"internal": "subscription_internal_id"
},
"renew_date": "2024-11-01T00:00:00.000Z",
"created": "2024-10-01T00:00:00.000Z"
}
}

Subscription Cancellation Response

{
"success": true,
"data": {
"partner": {
"name": "DashClicks Agency",
"phone": "+1234567890",
"email": "agency@dashclicks.com",
"account": "partner_account_id"
},
"buyer": {
"name": "Client Business Inc",
"phone": "+1987654321",
"email": "client@business.com",
"account": "buyer_account_id"
},
"product": {
"name": "SEO Management",
"tier": "Professional",
"interval": "month",
"interval_count": 1,
"amount": 29999,
"amount_due": 25499
},
"status": "canceled",
"team_tasks_pending": true,
"cancel_at_period_end": false,
"current_period_start": "2024-10-01T00:00:00.000Z",
"current_period_end": "2024-11-01T00:00:00.000Z",
"cancel_at": "2024-10-15T14:30:00.000Z",
"ended_at": "2024-10-15T14:30:00.000Z",
"subscription_id": {
"stripe": "sub_stripe_id_123",
"internal": "subscription_internal_id"
}
}
}

Request Parameters

Payment Retry Parameters

  • sub_id (path parameter) - MongoDB ObjectId of subscription to retry payment

Subscription Cancellation Parameters

  • sub_id (path parameter) - MongoDB ObjectId of subscription to cancel
  • immediate (query parameter) - Boolean flag for immediate vs period-end cancellation

Resume Cancellation Parameters

  • sub_id (path parameter) - MongoDB ObjectId of subscription to resume

Clear Subscription Parameters

  • sub_id (path parameter) - MongoDB ObjectId of subscription to clear

Error Handling

Payment Operations Error Scenarios

  • 404 Not Found: Subscription does not exist or not in valid state
  • 400 Bad Request: Invalid subscription ID or operation not permitted
  • 402 Payment Required: Payment processing failed due to payment method issues
  • 500 Internal Server Error: Stripe API failures or database transaction errors

Payment Retry Error Response

{
"success": false,
"message": "Payment failed: Your card was declined. Your request was in test mode, but used a non test card. For a list of valid test cards, visit: https://stripe.com/docs/testing",
"errno": 402
}

Cancellation Error Response

{
"success": false,
"message": "Cancellation failed: No such subscription: sub_invalid_id",
"errno": 400
}

Resume Error Response

{
"success": false,
"message": "Cancelled subscription not found",
"errno": 404
}

Usage Examples

Retry Payment for Past-Due Subscription

POST /v1/admin/billing/subscription/507f1f77bcf86cd799439011
Authorization: Bearer {admin_token}

Cancel Subscription at Period End

DELETE /v1/admin/billing/subscription/507f1f77bcf86cd799439011
Authorization: Bearer {admin_token}

Cancel Subscription Immediately

DELETE /v1/admin/billing/subscription/507f1f77bcf86cd799439011?immediate=true
Authorization: Bearer {admin_token}

Resume Canceled Subscription

PUT /v1/admin/billing/subscription/507f1f77bcf86cd799439011/resume
Authorization: Bearer {admin_token}

Clear Subscription Tasks

POST /v1/admin/billing/subscription/507f1f77bcf86cd799439011/clear
Authorization: Bearer {admin_token}
💬

Documentation Assistant

Ask me anything about the docs

Hi! I'm your documentation assistant. Ask me anything about the docs!

I can help you with:
- Code examples
- Configuration details
- Troubleshooting
- Best practices

Try asking: How do I configure the API?
09:31 AM