use std::sync::Arc;
use axum::{
extract::{Query, State},
http::StatusCode,
response::IntoResponse,
routing::{get, put},
Json, Router,
};
use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use crate::application::service::emv_qr_service::{
EmvPayloadAck, EmvQrConfigAck, EmvQrConfigInput, EmvQrService, EmvQrServiceError,
};
#[derive(Debug, Deserialize)]
pub struct EmvQrConfigBody {
#[serde(default)]
pub bank_account_id: Option<Uuid>,
pub merchant_name: String,
pub merchant_city: String,
#[serde(default = "default_country")]
pub country: String,
pub mcc: String,
#[serde(default = "default_gui")]
pub gui: String,
pub merchant_identifier: String,
#[serde(default = "default_currency")]
pub currency: String,
#[serde(default = "default_initiation")]
pub initiation: String,
}
fn default_country() -> String {
"ID".into()
}
fn default_gui() -> String {
"ID.CO.QRIS.WWW".into()
}
fn default_currency() -> String {
"IDR".into()
}
fn default_initiation() -> String {
"11".into()
}
#[derive(Debug, Deserialize)]
pub struct EmvQrPayloadQuery {
#[serde(default)]
pub bank_account_id: Option<Uuid>,
pub amount: Option<Decimal>,
pub currency: Option<String>,
pub reference: Option<String>,
}
#[derive(Debug, Serialize)]
struct ErrorBody {
error: String,
message: String,
}
fn error_response(e: &EmvQrServiceError) -> axum::response::Response {
let status = StatusCode::from_u16(e.http_status()).unwrap_or(StatusCode::INTERNAL_SERVER_ERROR);
(
status,
Json(ErrorBody {
error: e.code().to_string(),
message: e.to_string(),
}),
)
.into_response()
}
async fn upsert_config(
State(service): State<Arc<EmvQrService>>,
Json(body): Json<EmvQrConfigBody>,
) -> impl IntoResponse {
let input = EmvQrConfigInput {
bank_account_id: body.bank_account_id,
merchant_name: body.merchant_name,
merchant_city: body.merchant_city,
country: body.country,
mcc: body.mcc,
gui: body.gui,
merchant_identifier: body.merchant_identifier,
currency: body.currency,
initiation: body.initiation,
};
match service.upsert_config(input).await {
Ok(ack) => (
StatusCode::OK,
Json(EmvQrConfigAck {
config_id: ack.config_id,
bank_account_id: ack.bank_account_id,
}),
)
.into_response(),
Err(e) => error_response(&e),
}
}
async fn payload(
State(service): State<Arc<EmvQrService>>,
Query(q): Query<EmvQrPayloadQuery>,
) -> impl IntoResponse {
match service
.invoice_payload(
q.bank_account_id,
q.amount,
q.currency,
q.reference,
)
.await
{
Ok(ack) => (StatusCode::OK, Json(EmvPayloadAck {
payload: ack.payload,
currency: ack.currency,
initiation_method: ack.initiation_method,
}))
.into_response(),
Err(e) => error_response(&e),
}
}
pub fn create_emv_qr_routes(service: Arc<EmvQrService>) -> Router {
Router::new()
.route("/accounting/emv-qr/config", put(upsert_config))
.route("/accounting/emv-qr/payload", get(payload))
.with_state(service)
}