use crate::checkout::{Checkout, State};
use crate::context::Context;
use crate::customer::{Address, Contact};
use crate::error::{new_application_error, new_invalid_state_error, new_not_found_error, Error};
use crate::internal_context::InternalContext;
use crate::money::Currency;
use crate::payment::cancel as cancel_payment;
use crate::payment::process as process_payment;
use crate::payment::PaymentProcessor;
use crate::payment::{Initiator, Payment};
use crate::shipping::FulfillmentSelection;
pub async fn get_handler<C: Context + Send>(
ctx: &mut C,
id: &str,
) -> Result<Checkout<Payment<<C as PaymentProcessor>::Data>>, Error> {
let maybe_co = ctx.get_checkout(id).await?;
if maybe_co.is_none() {
return Err(new_not_found_error(
"the requested checkout could not be found",
));
}
let mut co = maybe_co.unwrap();
co.populate_associations(ctx).await?;
Ok(co)
}
pub async fn create_handler<C: Context + Send>(
ctx: &mut C,
currency: Currency,
contact: Option<Contact>,
shipping_address: Option<Address>,
) -> Result<Checkout<Payment<<C as PaymentProcessor>::Data>>, Error> {
let mut co = Checkout::new(ctx, currency, contact, shipping_address).await?;
co.populate_associations(ctx).await?;
Ok(co)
}
pub async fn add_item_handler<C: Context + Send>(
ctx: &mut C,
checkout_id: &str,
sku: String,
quantity: u64,
) -> Result<Checkout<Payment<<C as PaymentProcessor>::Data>>, Error> {
let maybe_co = ctx.get_checkout_for_update(&checkout_id).await?;
if maybe_co.is_none() {
return Err(new_not_found_error(
"the requested checkout could not be found",
));
}
let mut co = maybe_co.unwrap();
co.add_item(ctx, sku, quantity).await?;
co.populate_associations(ctx).await?;
Ok(co)
}
pub async fn remove_item_handler<C: Context + Send>(
ctx: &mut C,
checkout_id: &str,
sku: String,
quantity: u64,
) -> Result<Checkout<Payment<<C as PaymentProcessor>::Data>>, Error> {
let maybe_co = ctx.get_checkout_for_update(&checkout_id).await?;
if maybe_co.is_none() {
return Err(new_not_found_error(
"the requested checkout could not be found",
));
}
let mut co = maybe_co.unwrap();
co.remove_item(ctx, sku, quantity).await?;
co.populate_associations(ctx).await?;
Ok(co)
}
pub async fn confirm_items_handler<C: Context + Send>(
ctx: &mut C,
checkout_id: &str,
) -> Result<Checkout<Payment<<C as PaymentProcessor>::Data>>, Error> {
let maybe_co = ctx.get_checkout_for_update(&checkout_id).await?;
if maybe_co.is_none() {
return Err(new_not_found_error(
"the requested checkout could not be found",
));
}
let mut co = maybe_co.unwrap();
co.confirm_items(ctx).await?;
co.populate_associations(ctx).await?;
Ok(co)
}
pub async fn update_contact_handler<C: Context + Send>(
ctx: &mut C,
checkout_id: &str,
contact: Contact,
) -> Result<Checkout<Payment<<C as PaymentProcessor>::Data>>, Error> {
let maybe_co = ctx.get_checkout_for_update(&checkout_id).await?;
if maybe_co.is_none() {
return Err(new_not_found_error(
"the requested checkout could not be found",
));
}
let mut co = maybe_co.unwrap();
co.update_contact(ctx, contact).await?;
co.populate_associations(ctx).await?;
Ok(co)
}
pub async fn update_shipping_address_handler<C: Context + Send>(
ctx: &mut C,
checkout_id: &str,
shipping_address: Address,
) -> Result<Checkout<Payment<<C as PaymentProcessor>::Data>>, Error> {
let maybe_co = ctx.get_checkout_for_update(&checkout_id).await?;
if maybe_co.is_none() {
return Err(new_not_found_error(
"the requested checkout could not be found",
));
}
let mut co = maybe_co.unwrap();
co.update_shipping_address(ctx, shipping_address).await?;
co.populate_associations(ctx).await?;
Ok(co)
}
pub async fn update_fulfillment_handler<C: Context + Send>(
ctx: &mut C,
checkout_id: &str,
fulfillment: FulfillmentSelection,
) -> Result<Checkout<Payment<<C as PaymentProcessor>::Data>>, Error> {
let maybe_co = ctx.get_checkout_for_update(&checkout_id).await?;
if maybe_co.is_none() {
return Err(new_not_found_error(
"the requested checkout could not be found",
));
}
let mut co = maybe_co.unwrap();
co.update_fulfillment(ctx, fulfillment).await?;
co.populate_associations(ctx).await?;
Ok(co)
}
pub async fn initiate_payment_handler<C: Context + Send>(
ctx: &mut C,
checkout_id: &str,
args: <C as PaymentProcessor>::InitArgs,
) -> Result<Checkout<Payment<<C as PaymentProcessor>::Data>>, Error> {
let maybe_co = ctx.get_checkout_for_update(&checkout_id).await?;
if maybe_co.is_none() {
return Err(new_not_found_error(
"the requested checkout could not be found",
));
}
let mut co = maybe_co.unwrap();
co.initiate_payment(ctx, args).await?;
co.populate_associations(ctx).await?;
Ok(co)
}
pub async fn step_payment_handler<C: Context + Send>(
internal_ctx: &InternalContext,
ctx: &mut C,
checkout_id: &str,
args: <C as PaymentProcessor>::ProcessArgs,
) -> Result<Checkout<Payment<<C as PaymentProcessor>::Data>>, Error> {
let maybe_payment = get_checkout_payment_for_update(ctx, checkout_id).await?;
let maybe_co = ctx.get_checkout_for_update(checkout_id).await?;
if maybe_co.is_none() {
return Err(new_not_found_error(
"the requested checkout could not be found",
));
}
let mut co = maybe_co.unwrap();
match co.state {
State::PaymentInProgress{ reserved_since: _ } => {
let mut payment = maybe_payment.ok_or(new_not_found_error(
"the associated payment could not be found",
))?;
if &payment.id != co.payment_id.as_ref().unwrap() {
return Err(new_application_error("STALE_PAYMENT", "the associated payment was changed by another process while this transaction was in progress. please try again."));
}
process_payment(
internal_ctx,
ctx,
&mut payment,
Initiator::Checkout(&mut co),
args,
)
.await?;
co.populate_associations(ctx).await?;
Ok(co)
}
_ => Err(new_invalid_state_error(
"must be in the PaymentInProgress state",
)),
}
}
pub async fn initiate_order_handler<C: Context + Send>(
internal_ctx: &InternalContext,
ctx: &mut C,
checkout_id: &str,
args: <C as PaymentProcessor>::InitArgs,
) -> Result<Checkout<Payment<<C as PaymentProcessor>::Data>>, Error> {
let maybe_co = ctx.get_checkout_for_update(&checkout_id).await?;
if maybe_co.is_none() {
return Err(new_not_found_error(
"the requested checkout could not be found",
));
}
let mut co = maybe_co.unwrap();
co.initiate_order(internal_ctx, ctx, args).await?;
co.populate_associations(ctx).await?;
Ok(co)
}
pub async fn release_handler<C: Context + Send>(
internal_ctx: &InternalContext,
ctx: &mut C,
checkout_id: &str,
) -> Result<Checkout<Payment<<C as PaymentProcessor>::Data>>, Error> {
let maybe_payment = get_checkout_payment_for_update(ctx, checkout_id).await?;
let maybe_co = ctx.get_checkout_for_update(checkout_id).await?;
if maybe_co.is_none() {
return Err(new_not_found_error(
"the requested checkout could not be found",
));
}
let mut co = maybe_co.unwrap();
match co.state {
State::Shopping => {}
State::ItemsConfirmed{ reserved_since: _ } => {
co.release(ctx).await?;
}
State::PaymentInProgress{ reserved_since: _ } => {
let mut payment = maybe_payment.ok_or(new_not_found_error(
"the associated payment could not be found",
))?;
if &payment.id != co.payment_id.as_ref().unwrap() {
return Err(new_application_error("STALE_PAYMENT", "the associated payment was changed by another process while this transaction was in progress. please try again."));
}
cancel_payment(
internal_ctx,
ctx,
&mut payment,
Initiator::Checkout(&mut co),
)
.await?;
}
_ => {
return Err(new_invalid_state_error(
"must be in one of Shopping, ItemsConfirmed or PaymentInProgress states",
))
}
}
co.populate_associations(ctx).await?;
Ok(co)
}
async fn get_checkout_payment_for_update<C: Context + Send>(
ctx: &mut C,
checkout_id: &str,
) -> Result<Option<Payment<<C as PaymentProcessor>::Data>>, Error> {
let maybe_co: Option<Checkout<Payment<<C as PaymentProcessor>::Data>>> =
ctx.get_checkout(checkout_id).await?;
if maybe_co.is_none() {
return Err(new_not_found_error(
"the requested checkout could not be found",
));
}
let co = maybe_co.unwrap();
if co.payment_id.is_none() {
return Ok(None);
}
ctx.get_payment_for_update(co.payment_id.as_ref().unwrap())
.await
}