worldline 0.1.0

A library for integrating with Worldline's Bambora API.
Documentation
use crate::{
    currency::Currency, error::Error, order::Order, payment_method::PaymentMethod, Gateway,
    BASE_URL,
};
use rust_decimal::prelude::*;
use serde::Serialize;

impl Gateway {
    pub async fn create_order<'a>(
        &self,
        order_id: &'a str,
        accept_url: Option<&'a str>,
        cancel_url: Option<&'a str>,
        amount: &'a Decimal,
        vat: &'a Decimal,
        currency: &'a Currency,
        callbacks: Option<Vec<&'a str>>,
        language: &'a str,
        paymentmethods: Vec<&'a PaymentMethod>,
    ) -> Result<Order, Error> {
        #[derive(Serialize)]
        struct Request<'a> {
            order: OrderRequest<'a>,

            #[serde(skip_serializing_if = "Option::is_none")]
            url: Option<Url<'a>>,

            paymentwindow: PaymentWindow<'a>,
        }

        #[derive(Serialize)]
        struct OrderRequest<'a> {
            amount: u64,
            id: &'a str,
            vatamount: u64,
            currency: &'a str,
        }

        #[derive(Serialize)]
        struct Callback<'a> {
            url: &'a str,
        }

        #[derive(Serialize)]
        struct Url<'a> {
            accept: Option<&'a str>,

            cancel: Option<&'a str>,

            #[serde(skip_serializing_if = "Option::is_none")]
            callbacks: Option<Vec<Callback<'a>>>,
        }

        #[derive(Serialize)]
        struct PaymentMethodObject<'a> {
            id: &'a PaymentMethod,
            action: &'a str,
        }

        #[derive(Serialize)]
        struct PaymentWindow<'a> {
            language: &'a str,
            paymentmethods: Vec<PaymentMethodObject<'a>>,
        }

        let (currency, decimals) = match currency {
            Currency::NOK => ("NOK", 2),
            Currency::SEK => ("SEK", 2),
            Currency::DKK => ("DKK", 2),
            Currency::ISK => ("ISK", 0),
            Currency::GBP => ("GBP", 2),
            Currency::EUR => ("EUR", 2),
        };

        // Get cents from decimal.
        let cents_modifier = Decimal::from(10i32.pow(decimals));
        let amount = amount * cents_modifier;
        let vat = vat * cents_modifier;

        let url = if accept_url != None || cancel_url != None || callbacks != None {
            Some(Url {
                accept: accept_url,
                cancel: cancel_url,
                callbacks: match callbacks {
                    Some(c) => Some(
                        c.iter()
                            .map(|c| Callback { url: *c })
                            .collect::<Vec<Callback>>(),
                    ),
                    None => None,
                },
            })
        } else {
            None
        };

        let body = Request {
            order: OrderRequest {
                amount: match amount.to_u64() {
                    Some(a) => a,
                    None => {
                        return Err(Error::Unspecified(format!(
                            "could not convert amount \"{}\" to u64",
                            amount
                        )))
                    }
                },
                id: order_id,
                vatamount: match vat.to_u64() {
                    Some(a) => a,
                    None => {
                        return Err(Error::Unspecified(format!(
                            "could not convert vat \"{}\" to u64",
                            amount
                        )))
                    }
                },
                currency,
            },
            url,
            paymentwindow: PaymentWindow {
                language,
                paymentmethods: paymentmethods
                    .into_iter()
                    .map(|p| PaymentMethodObject {
                        id: p,
                        action: "include",
                    })
                    .collect::<Vec<PaymentMethodObject>>(),
            },
        };

        let url = format!("{}/sessions", BASE_URL);
        let res: Order = self.send(&url, &body).await?;
        Ok(res)
    }
}