Skip to content

Commit

Permalink
Add auto-TopUp requests
Browse files Browse the repository at this point in the history
  • Loading branch information
aarashy committed Mar 22, 2024
1 parent f9254d9 commit 51bcf44
Show file tree
Hide file tree
Showing 3 changed files with 207 additions and 11 deletions.
198 changes: 197 additions & 1 deletion src/client/customers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ pub enum LedgerEntryRequest<'a> {
}

/// Optional invoicing settings for a credit purchase.
#[derive(Debug, Clone, Default, PartialEq, Eq, Hash, Serialize)]
#[derive(Debug, Clone, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct CreditLedgerInvoiceSettingsRequestParams<'a> {
/// Whether the credits purchase invoice should auto collect with the customer's saved payment
/// method.
Expand All @@ -282,6 +282,25 @@ pub struct CreditLedgerInvoiceSettingsRequestParams<'a> {
pub require_successful_payment: Option<bool>,
}

/// Optional invoicing settings for a credit purchase.
/// Unlike CreditLedgerInvoiceSettingsRequestParams, this struct is owned.
#[derive(Debug, Clone, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct CreditLedgerInvoiceSettings {
/// Whether the credits purchase invoice should auto collect with the customer's saved payment
/// method.
pub auto_collection: bool,
/// The difference between the invoice date and the issue date for the invoice. If due on issue,
/// set this to `0`.
pub net_terms: u64,
/// An optional memo to display on the invoice
#[serde(skip_serializing_if = "Option::is_none")]
pub memo: Option<String>,
/// Whether the credits should be withheld from the customer account until the invoice is paid.
/// This applies primarily to stripe invoicing.
#[serde(skip_serializing_if = "Option::is_none")]
pub require_successful_payment: Option<bool>,
}

/// The parameters used to create a customer credit ledger entry.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
pub struct AddIncrementCreditLedgerEntryRequestParams<'a> {
Expand Down Expand Up @@ -341,6 +360,21 @@ pub struct CustomerCreditBlock {
pub expiry_date: Option<OffsetDateTime>,
/// The price per credit.
pub per_unit_cost_basis: Option<String>,
/// The status of the credit block. Credit blocks are initialized into `pending_payment`
/// when require_successful_payment is set in [`CreditLedgerInvoiceSettings`].
pub status: CreditBlockStatus,
}

/// The status of the credit block. Credit blocks are initialized into `pending_payment`
/// when require_successful_payment is set in [`CreditLedgerInvoiceSettings`].
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
pub enum CreditBlockStatus {
/// A credit block that should count to the total balance.
#[serde(rename = "active")]
Active,
/// A credit block which hasn't been marked as paid.
#[serde(rename = "pending_payment")]
PendingPayment,
}

/// The type of ledger entry
Expand Down Expand Up @@ -479,6 +513,45 @@ pub struct CustomerCostParamsFilter<'a> {
pub group_by: Option<&'a str>,
}

/// Configures automatic payments for the customer
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
pub struct CreateTopUpRequest<'a> {
/// The currency used for this topup.
pub currency: CurrencyCode,
/// The threshold at which to initiate the topup payment.
pub threshold: &'a str,
/// The amount that should be purchase.
pub amount: &'a str,
/// The cost basis of the credits purchase.
pub per_unit_cost_basis: &'a str,
/// Additional settings for configuring the invoice
pub invoice_settings: Option<CreditLedgerInvoiceSettingsRequestParams<'a>>,
}

/// The response for a user's listing topups.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct ListTopUpsResponse {
/// The list of topups.
pub data: Vec<TopUp>,
}

/// Configures an external payment or invoicing solution for a customer.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct TopUp {
/// The Orb-assigned ID of this topup.
pub id: String,
/// The currency used for this topup.
pub currency: CurrencyCode,
/// The threshold at which to initiate the topup payment.
pub threshold: String,
/// The amount that should be purchase.
pub amount: String,
/// The cost basis of the credits purchase.
pub per_unit_cost_basis: String,
/// Additional settings for configuring the invoice
pub invoice_settings: Option<CreditLedgerInvoiceSettings>,
}

trait Filterable<T> {
/// Apply the filter to a request.
fn apply(self, filter: &T) -> Self;
Expand Down Expand Up @@ -828,6 +901,24 @@ impl Client {
self.send_request(req).await
}

/// Create a new ledger entry for the specified customer's balance by external ID
pub async fn create_ledger_entry_by_external_id(
&self,
external_id: &str,
entry: &LedgerEntryRequest<'_>,
) -> Result<LedgerEntry, Error> {
let req = self.build_request(
Method::POST,
CUSTOMERS_PATH
.chain_one("external_customer_id")
.chain_one(external_id)
.chain_one("credits")
.chain_one("ledger_entry"),
);
let req = req.json(entry);
self.send_request(req).await
}

/// Fetch a day-by-day snapshot of a customer's costs.
pub async fn get_customer_costs(
&self,
Expand Down Expand Up @@ -857,4 +948,109 @@ impl Client {
let res: ArrayResponse<CustomerCostBucket> = self.send_request(req).await?;
Ok(res.data)
}

/// List top-ups for a customer. There is a maximum of one topup per currency, so in most
/// cases, pagination is not important.
pub async fn get_customer_topup(&self, id: &str) -> Result<ListTopUpsResponse, Error> {
let req = self.build_request(
Method::GET,
CUSTOMERS_PATH
.chain_one(id)
.chain_one("credits")
.chain_one("top_ups"),
);
let res: ListTopUpsResponse = self.send_request(req).await?;
Ok(res)
}

/// List top-ups for a customer by external id. There is a maximum of one topup per currency.
pub async fn get_customer_topup_by_external_id(
&self,
external_id: &str,
) -> Result<ListTopUpsResponse, Error> {
let req = self.build_request(
Method::GET,
CUSTOMERS_PATH
.chain_one("external_customer_id")
.chain_one(external_id)
.chain_one("credits")
.chain_one("top_ups"),
);
let res: ListTopUpsResponse = self.send_request(req).await?;
Ok(res)
}

/// Create top-up for a customer. There is a maximum of one topup per currency.
pub async fn create_customer_topup<'a>(
&self,
id: &str,
body: CreateTopUpRequest<'a>,
) -> Result<TopUp, Error> {
let req = self.build_request(
Method::POST,
CUSTOMERS_PATH
.chain_one(id)
.chain_one("credits")
.chain_one("top_ups"),
);
let req: RequestBuilder = req.json(&body);
let res: TopUp = self.send_request(req).await?;
Ok(res)
}

/// Create top-up for a customer by external id. There is a maximum of one topup per currency.
pub async fn create_customer_topup_by_external_id<'a>(
&self,
external_id: &str,
body: CreateTopUpRequest<'a>,
) -> Result<TopUp, Error> {
let req = self.build_request(
Method::POST,
CUSTOMERS_PATH
.chain_one("external_customer_id")
.chain_one(external_id)
.chain_one("credits")
.chain_one("top_ups"),
);
let req: RequestBuilder = req.json(&body);
let res: TopUp = self.send_request(req).await?;
Ok(res)
}

/// Create top-up for a customer. There is a maximum of one topup per currency.
pub async fn delete_customer_topup<'a>(
&self,
user_id: &str,
top_up_id: &str,
) -> Result<TopUp, Error> {
let req = self.build_request(
Method::DELETE,
CUSTOMERS_PATH
.chain_one(user_id)
.chain_one("credits")
.chain_one("top_ups")
.chain_one(top_up_id),
);
let res: TopUp = self.send_request(req).await?;
Ok(res)
}

/// Create top-up for a customer by external id. There is a maximum of one topup per currency.
pub async fn delete_customer_topup_by_external_id<'a>(
&self,
user_external_id: &str,
top_up_id: &str,
) -> Result<TopUp, Error> {
let req = self.build_request(
Method::DELETE,
CUSTOMERS_PATH
.chain_one("external_customer_id")
.chain_one(user_external_id)
.chain_one("credits")
.chain_one("top_ups")
.chain_one(top_up_id),
);
let res: TopUp = self.send_request(req).await?;
Ok(res)
}
}
5 changes: 2 additions & 3 deletions src/client/invoices.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use std::collections::BTreeMap;
use futures_core::Stream;
use reqwest::Method;
use serde::{Deserialize, Serialize};
use time::OffsetDateTime;
use time::{Date, OffsetDateTime};

use crate::client::customers::CustomerId;
use crate::client::Client;
Expand Down Expand Up @@ -128,8 +128,7 @@ impl InvoiceStatusFilter {
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
pub struct MarkInvoiceAsPaidBody<'a> {
/// A date string to specify the date of the payment.
#[serde(with = "time::serde::rfc3339")]
pub payment_received_date: OffsetDateTime,
pub payment_received_date: Date,

/// An optional external ID to associate with the payment.
pub external_id: Option<&'a str>,
Expand Down
15 changes: 8 additions & 7 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,15 @@ mod util;

pub use client::customers::{
AddIncrementCreditLedgerEntryRequestParams, AddVoidCreditLedgerEntryRequestParams, Address,
AddressRequest, CostViewMode, CreateCustomerRequest, CreditLedgerInvoiceSettingsRequestParams,
Customer, CustomerCostBucket, CustomerCostItem, CustomerCostParams, CustomerCostParamsFilter,
CustomerCostPriceBlock, CustomerCostPriceBlockMatrixPrice,
CustomerCostPriceBlockMatrixPriceConfig, CustomerCostPriceBlockMatrixPriceValue,
CustomerCostPriceBlockPrice, CustomerCostPriceBlockPriceGroup, CustomerCostPriceBlockUnitPrice,
AddressRequest, CostViewMode, CreateCustomerRequest, CreateTopUpRequest, CreditBlockStatus,
CreditLedgerInvoiceSettingsRequestParams, Customer, CustomerCostBucket, CustomerCostItem,
CustomerCostParams, CustomerCostParamsFilter, CustomerCostPriceBlock,
CustomerCostPriceBlockMatrixPrice, CustomerCostPriceBlockMatrixPriceConfig,
CustomerCostPriceBlockMatrixPriceValue, CustomerCostPriceBlockPrice,
CustomerCostPriceBlockPriceGroup, CustomerCostPriceBlockUnitPrice,
CustomerCostPriceBlockUnitPriceConfig, CustomerCreditBlock, CustomerId,
CustomerPaymentProviderRequest, LedgerEntry, LedgerEntryRequest, PaymentProvider,
UpdateCustomerRequest, VoidReason,
CustomerPaymentProviderRequest, LedgerEntry, LedgerEntryRequest, ListTopUpsResponse,
PaymentProvider, TopUp, UpdateCustomerRequest, VoidReason,
};
pub use client::events::{
AmendEventRequest, Event, EventPropertyValue, EventSearchParams, IngestEventDebugResponse,
Expand Down

0 comments on commit 51bcf44

Please sign in to comment.