Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 1 addition & 20 deletions .github/workflows/PR.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: Rust test on PR fork
on:
pull_request_target:
types: [opened, synchronize]
types: [opened, synchronize]
env:
CARGO_TERM_COLOR: always
PAYSTACK_API_KEY: ${{secrets.PAYSTACK_API_KEY}}
Expand Down Expand Up @@ -35,22 +35,3 @@ jobs:
run: cargo build --verbose
- name: Run tests
run: cargo test --verbose

coverage:
runs-on: ubuntu-latest
env:
CARGO_TERM_COLOR: always
steps:
- uses: actions/checkout@v4
- name: Install Rust
run: rustup update stable
- name: Install cargo-llvm-cov
uses: taiki-e/install-action@cargo-llvm-cov
- name: Generate code coverage
run: cargo llvm-cov --all-features --workspace --lcov --output-path lcov.info
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
with:
files: lcov.info
fail_ci_if_error: true

3 changes: 3 additions & 0 deletions src/endpoints/subaccount.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,11 @@ use std::sync::Arc;
/// A struct to hold all functions in the subaccount API route
#[derive(Debug, Clone)]
pub struct SubaccountEndpoints<T: HttpClient + Default> {
/// Paystack API Key
key: String,
/// Base URL for the transaction route
base_url: String,
/// Http client for the route
http: Arc<T>,
}

Expand Down
3 changes: 3 additions & 0 deletions src/endpoints/terminal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@ use crate::{EventRequest, HttpClient, PaystackResult, SendEventResponseData};
/// A struct to hold all the functions of the terminal API endpoint
#[derive(Debug, Clone)]
pub struct TerminalEndpoints<T: HttpClient + Default> {
/// Paystack API Key
key: String,
/// Base URL for the transaction route
base_url: String,
/// Http client for the route
http: Arc<T>,
}

Expand Down
34 changes: 17 additions & 17 deletions src/endpoints/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
//! The Transaction route allows to create and manage payments on your integration.

use crate::{
ChargeRequest, Currency, ExportTransactionData, HttpClient, PartialDebitTransactionRequest,
PaystackAPIError, PaystackResult, Response, Status, TransactionRequest,
TransactionResponseData, TransactionStatusData, TransactionTimelineData, TransactionTotalData,
ChargeRequest, ChargeResponseData, Currency, ExportTransactionData, HttpClient,
PartialDebitTransactionRequest, PaystackAPIError, PaystackResult, Response, Status,
TransactionIdentifier, TransactionRequest, TransactionResponseData, TransactionStatusData,
TransactionTimelineData, TransactionTotalData,
};
use std::sync::Arc;

Expand Down Expand Up @@ -101,6 +102,8 @@ impl<T: HttpClient + Default> TransactionEndpoints<T> {

let response = self.http.get(&url, &self.key, Some(&query)).await;

dbg!("{:#?}", &response);

match response {
Ok(response) => {
let parsed_response: Response<Vec<TransactionStatusData>> =
Expand All @@ -118,7 +121,7 @@ impl<T: HttpClient + Default> TransactionEndpoints<T> {
/// This method take the ID of the desired transaction as a parameter
pub async fn fetch_transactions(
&self,
transaction_id: u32,
transaction_id: u64,
) -> PaystackResult<TransactionStatusData> {
let url = format!("{}/{}", self.base_url, transaction_id);

Expand All @@ -142,7 +145,7 @@ impl<T: HttpClient + Default> TransactionEndpoints<T> {
pub async fn charge_authorization(
&self,
charge_request: ChargeRequest,
) -> PaystackResult<TransactionStatusData> {
) -> PaystackResult<ChargeResponseData> {
let url = format!("{}/charge_authorization", self.base_url);
let body = serde_json::to_value(charge_request)
.map_err(|e| PaystackAPIError::Transaction(e.to_string()))?;
Expand All @@ -151,9 +154,8 @@ impl<T: HttpClient + Default> TransactionEndpoints<T> {

match response {
Ok(response) => {
let parsed_response: Response<TransactionStatusData> =
serde_json::from_str(&response)
.map_err(|e| PaystackAPIError::Transaction(e.to_string()))?;
let parsed_response: Response<ChargeResponseData> = serde_json::from_str(&response)
.map_err(|e| PaystackAPIError::Charge(e.to_string()))?;

Ok(parsed_response)
}
Expand All @@ -163,20 +165,18 @@ impl<T: HttpClient + Default> TransactionEndpoints<T> {

/// View the timeline of a transaction.
///
/// This method takes in the Transaction id or reference as a parameter
/// This method takes in the TransactionIdentifier as a parameter
pub async fn view_transaction_timeline(
&self,
id: Option<u32>,
reference: Option<&str>,
identifier: TransactionIdentifier,
) -> PaystackResult<TransactionTimelineData> {
// This is a hacky implementation to ensure that the transaction reference or id is not empty.
// If they are empty, a new url without them as parameter is created.
let url = match (id, reference) {
(Some(id), None) => Ok(format!("{}/timeline/{}", self.base_url, id)),
(None, Some(reference)) => Ok(format!("{}/timeline/{}", self.base_url, &reference)),
_ => Err(PaystackAPIError::Transaction(
"Transaction Id or Reference is need to view transaction timeline".to_string(),
)),
let url = match identifier {
TransactionIdentifier::Id(id) => Ok(format!("{}/timeline/{}", self.base_url, id)),
TransactionIdentifier::Reference(reference) => {
Ok(format!("{}/timeline/{}", self.base_url, &reference))
}
}?; // propagate the error upstream

let response = self.http.get(&url, &self.key, None).await;
Expand Down
3 changes: 3 additions & 0 deletions src/endpoints/transaction_split.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,11 @@ use std::sync::Arc;
/// A struct to hold all the functions of the transaction split API endpoint
#[derive(Debug, Clone)]
pub struct TransactionSplitEndpoints<T: HttpClient + Default> {
/// Paystack API Key
key: String,
/// Base URL for the transaction route
base_url: String,
/// Http client for the route
http: Arc<T>,
}

Expand Down
32 changes: 32 additions & 0 deletions src/models/authorization.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use serde::{Deserialize, Serialize};

/// This struct represents the authorization data of the transaction status response
#[derive(Debug, Deserialize, Serialize, Clone, Default)]
pub struct Authorization {
/// Authorization code generated for the Transaction.
pub authorization_code: Option<String>,
/// Bin number for Transaction authorization.
pub bin: Option<String>,
/// Last 4 digits of authorized card.
pub last4: Option<String>,
/// Authorized card expiry month.
pub exp_month: Option<String>,
/// Authorized card expiry year.
pub exp_year: Option<String>,
/// Authorization channel. It could be `card` or `bank`.
pub channel: Option<String>,
/// Type of card used in the Authorization
pub card_type: Option<String>,
/// Name of bank associated with the Authorization.
pub bank: Option<String>,
/// Country code of the Authorization.
pub country_code: Option<String>,
/// Brand of of the Authorization if it is a card.
pub brand: Option<String>,
/// Specifies if the Authorization is reusable.
pub reusable: Option<bool>,
/// Signature of the Authorization.
pub signature: Option<String>,
/// Name of the account associated with the authorization.
pub account_name: Option<String>,
}
24 changes: 23 additions & 1 deletion src/models/charge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@

use crate::{Channel, Currency};
use derive_builder::Builder;
use serde::Serialize;
use serde::{Deserialize, Serialize};

use super::{Authorization, Customer};

/// This struct is used to create a charge body for creating a Charge Authorization using the Paystack API.
/// The struct is constructed using the `ChargeBodyBuilder`
Expand Down Expand Up @@ -49,3 +51,23 @@ pub struct ChargeRequest {
#[builder(default = "None")]
queue: Option<bool>,
}

/// This struct represents the charge response
#[derive(Deserialize, Serialize, Debug, Clone, Default)]
pub struct ChargeResponseData {
pub amount: u64,
pub currency: String,
pub transaction_date: String,
pub status: String,
pub reference: String,
pub metadata: Option<String>,
pub gateway_response: String,
pub message: Option<String>,
pub channel: String,
pub ip_address: Option<String>,
pub fees: u64,
pub authorization: Authorization,
pub customer: Customer,
pub plan: Option<String>,
pub id: Option<u64>,
}
8 changes: 3 additions & 5 deletions src/models/customer.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
use serde::{Deserialize, Serialize};

/// This struct represents the Paystack customer data
#[derive(Debug, Deserialize, Serialize, Clone)]
#[derive(Debug, Deserialize, Serialize, Clone, Default)]
pub struct Customer {
/// Customer's Id.
pub id: Option<u32>,
pub id: u32,
/// Customer's first name.
pub first_name: Option<String>,
/// Customer's last name.
pub last_name: Option<String>,
/// Customer's email address.
pub email: Option<String>,
pub email: String,
/// Customer's code.
pub customer_code: String,
/// Customer's phone number.
pub phone: Option<String>,
/// Customer's metadata.
pub metadata: Option<String>,
/// Customer's risk action.
pub risk_action: Option<String>,
/// Customer's phone number in international format.
Expand Down
2 changes: 2 additions & 0 deletions src/models/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod authorization;
pub mod bearer;
pub mod channel;
pub mod charge;
Expand All @@ -12,6 +13,7 @@ pub mod transaction;
pub mod transaction_split;

// public re-export
pub use authorization::*;
pub use bearer::*;
pub use channel::*;
pub use charge::*;
Expand Down
31 changes: 0 additions & 31 deletions src/models/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,34 +38,3 @@ pub struct Meta {
#[serde(deserialize_with = "string_or_number_to_u16")]
pub page_count: u16,
}

/// This struct represents the authorization data of the transaction status response
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct Authorization {
/// Authorization code generated for the Transaction.
pub authorization_code: Option<String>,
/// Bin number for Transaction authorization.
pub bin: Option<String>,
/// Last 4 digits of authorized card.
pub last4: Option<String>,
/// Authorized card expiry month.
pub exp_month: Option<String>,
/// Authorized card expiry year.
pub exp_year: Option<String>,
/// Authorization channel. It could be `card` or `bank`.
pub channel: Option<String>,
/// Type of card used in the Authorization
pub card_type: Option<String>,
/// Name of bank associated with the Authorization.
pub bank: Option<String>,
/// Country code of the Authorization.
pub country_code: Option<String>,
/// Brand of of the Authorization if it is a card.
pub brand: Option<String>,
/// Specifies if the Authorization is reusable.
pub reusable: Option<bool>,
/// Signature of the Authorization.
pub signature: Option<String>,
/// Name of the account associated with the authorization.
pub account_name: Option<String>,
}
28 changes: 18 additions & 10 deletions src/models/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,35 +87,35 @@ pub struct TransactionResponseData {
#[derive(Deserialize, Serialize, Debug, Clone, Default)]
pub struct TransactionStatusData {
/// Id of the Transaction
pub id: Option<u32>,
pub id: u64,
/// Status of the Transaction. It can be `success`, `abandoned` or `failed`
pub status: Option<String>,
pub status: String,
/// Reference of the Transaction
pub reference: Option<String>,
pub reference: String,
/// Amount of the transaction in the lowest denomination of the currency e.g. Kobo for NGN and cent for USD.
pub amount: Option<u32>,
pub amount: u32,
/// Message from the transaction.
pub message: Option<String>,
/// Response from the payment gateway.
pub gateway_response: Option<String>,
pub gateway_response: String,
/// Time the Transaction was completed.
pub paid_at: Option<String>,
/// Time the Transaction was created.
pub created_at: Option<String>,
pub created_at: String,
/// Transaction channel. It can be `card` or `bank`.
pub channel: Option<String>,
pub channel: String,
/// Currency code of the Transaction e.g. `NGN for Nigerian Naira` and `USD for US Dollar`.
pub currency: Option<String>,
pub currency: String,
/// IP address of the computers the Transaction has passed through.
pub ip_address: Option<String>,
/// Meta data associated with the Transaction.
pub metadata: Option<String>,
/// Transaction fees to override the default fees specified in the integration.
pub fees: Option<i32>,
/// Transaction customer data.
pub customer: Option<Customer>,
pub customer: Customer,
/// Transaction authorization data.
pub authorization: Option<Authorization>,
pub authorization: Authorization,
}

/// This struct represents the transaction timeline data.
Expand Down Expand Up @@ -186,6 +186,14 @@ pub struct ExportTransactionData {
pub path: String,
}

/// Transaction identifier.
///
/// It can either be a transaction reference or a transaction ID
pub enum TransactionIdentifier {
Id(u64),
Reference(String),
}

#[cfg(test)]
mod test {
use super::*;
Expand Down
24 changes: 5 additions & 19 deletions tests/api/charge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,28 +28,14 @@ async fn charge_authorization_succeeds() -> Result<(), Box<dyn Error>> {

// Assert
assert!(charge_response.status);
assert_eq!(charge_response.data.customer.email, "susanna@example.net");
assert_eq!(
charge_response.data.customer.unwrap().email.unwrap(),
"susanna@example.net"
charge_response.data.authorization.clone().channel,
Some("card".into())
);
assert_eq!(
charge_response
.data
.authorization
.clone()
.unwrap()
.channel
.unwrap(),
"card"
);
assert_eq!(
charge_response
.data
.authorization
.unwrap()
.authorization_code
.unwrap(),
"AUTH_ik4t69fo2y"
charge_response.data.authorization.authorization_code,
Some("AUTH_ik4t69fo2y".into())
);

Ok(())
Expand Down
Loading
Loading