use crate::checkout::Checkout;
use crate::context::Context;
use crate::error::new_application_error;
use crate::error::new_invalid_state_error;
use crate::error::new_not_found_error;
use crate::error::Error;
use crate::internal_context::InternalContext;
use crate::payment::cancel as cancel_payment;
use crate::payment::process as process_payment;
use crate::payment::Initiator;
use crate::payment::Payment;
use crate::payment::PaymentProcessor;
use super::reconcile;
use super::LookupKey;
use super::Order;
use super::OrderState;
pub async fn get_handler<C: Context + Send>(
ctx: &mut C,
id: &str,
) -> Result<Order<Payment<<C as PaymentProcessor>::Data>>, Error> {
let maybe_od = ctx.get_order(id).await?;
if maybe_od.is_none() {
return Err(new_not_found_error(
"the requested order could not be found",
));
}
let mut od = maybe_od.unwrap();
od.populate_associations(ctx).await?;
Ok(od)
}
pub async fn step_payment_handler<C: Context + Send>(
internal_ctx: &InternalContext,
ctx: &mut C,
order_id: &str,
args: <C as PaymentProcessor>::ProcessArgs,
) -> Result<Order<Payment<<C as PaymentProcessor>::Data>>, Error> {
let maybe_payment = get_order_payment_for_update(ctx, order_id).await?;
let maybe_od = ctx.get_order_for_update(order_id).await?;
if maybe_od.is_none() {
return Err(new_not_found_error(
"the requested order could not be found",
));
}
let mut od = maybe_od.unwrap();
match od.state {
OrderState::PaymentInProgress { reserved_since: _ } => {
let mut payment = maybe_payment.ok_or(new_not_found_error(
"the associated payment could not be found",
))?;
if &payment.id != od.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::Order(&mut od),
args,
)
.await?;
od.populate_associations(ctx).await?;
Ok(od)
}
_ => Err(new_invalid_state_error(
"order must be in the PaymentInProgress state",
)),
}
}
pub async fn confirm_handler<C: Context + Send>(
internal_ctx: &InternalContext,
ctx: &mut C,
order_id: &str,
) -> Result<Order<Payment<<C as PaymentProcessor>::Data>>, Error> {
let maybe_od = ctx.get_order_for_update(order_id).await?;
if maybe_od.is_none() {
return Err(new_not_found_error(
"the requested order could not be found",
));
}
let mut od = maybe_od.unwrap();
od.confirm(internal_ctx, ctx).await?;
od.populate_associations(ctx).await?;
Ok(od)
}
pub async fn complete_handler<C: Context + Send>(
internal_ctx: &InternalContext,
ctx: &mut C,
order_id: &str,
) -> Result<Order<Payment<<C as PaymentProcessor>::Data>>, Error> {
let maybe_od = ctx.get_order_for_update(order_id).await?;
if maybe_od.is_none() {
return Err(new_not_found_error(
"the requested order could not be found",
));
}
let mut od = maybe_od.unwrap();
od.complete(internal_ctx, ctx).await?;
od.populate_associations(ctx).await?;
Ok(od)
}
pub async fn cancel_handler<C: Context + Send>(
internal_ctx: &InternalContext,
ctx: &mut C,
order_id: &str,
) -> Result<Order<Payment<<C as PaymentProcessor>::Data>>, Error> {
let maybe_payment = get_order_payment_for_update(ctx, order_id).await?;
let maybe_od = ctx.get_order_for_update(order_id).await?;
if maybe_od.is_none() {
return Err(new_not_found_error(
"the requested order could not be found",
));
}
let mut od = maybe_od.unwrap();
match od.state {
OrderState::Cancelled => {}
OrderState::PaymentInProgress { reserved_since: _ } => {
let mut payment = maybe_payment.ok_or(new_not_found_error(
"the associated payment could not be found",
))?;
if &payment.id != od.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::Order(&mut od)).await?;
}
_ => {
od.cancel(internal_ctx, ctx).await?;
}
}
od.populate_associations(ctx).await?;
Ok(od)
}
pub async fn reconcile_handler<C: Context + Send>(
ctx: &mut C,
lookup_key: &LookupKey,
) -> Result<(), Error> {
let maybe_order: Option<Order<Payment<<C as PaymentProcessor>::Data>>> = match &lookup_key {
LookupKey::CheckoutId(checkout_id) => get_order_by_checkout_id(ctx, checkout_id).await?,
LookupKey::OrderId(order_id) => ctx.get_order_for_update(order_id).await?,
};
if maybe_order.is_none() {
return Err(new_not_found_error(
"the requested order could not be found",
));
}
let mut od = maybe_order.unwrap();
reconcile(ctx, &mut od).await
}
async fn get_order_by_checkout_id<C: Context + Send>(
ctx: &mut C,
checkout_id: &str,
) -> Result<Option<Order<Payment<<C as PaymentProcessor>::Data>>>, Error> {
let maybe_checkout: Option<Checkout<Payment<<C as PaymentProcessor>::Data>>> =
ctx.get_checkout_for_update(checkout_id).await?;
if maybe_checkout.is_none() {
return Err(new_not_found_error(
"the associated checkout could not be found",
));
}
let checkout = maybe_checkout.unwrap();
if checkout.order_id.is_none() {
return Err(new_application_error(
"INVALID_ASSOCIATED_CHECKOUT",
"there is no order associated with the checkout",
));
}
ctx.get_order_for_update(&checkout.order_id.unwrap()).await
}
async fn get_order_payment_for_update<C: Context + Send>(
ctx: &mut C,
order_id: &str,
) -> Result<Option<Payment<<C as PaymentProcessor>::Data>>, Error> {
let maybe_od: Option<Order<Payment<<C as PaymentProcessor>::Data>>> =
ctx.get_order(order_id).await?;
if maybe_od.is_none() {
return Err(new_not_found_error(
"the requested order could not be found",
));
}
let od = maybe_od.unwrap();
if od.payment_id.is_none() {
return Ok(None);
}
ctx.get_payment_for_update(od.payment_id.as_ref().unwrap())
.await
}