diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 8ce7e1a..655634b 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -1,7 +1,7 @@ name: Rust on: push: - branches: [ "main", "master" ] + branches: ["main", "master"] env: CARGO_TERM_COLOR: always PAYSTACK_API_KEY: ${{secrets.PAYSTACK_API_KEY}} @@ -12,25 +12,8 @@ jobs: build-and-test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - name: Build - 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 \ No newline at end of file + - uses: actions/checkout@v3 + - name: Build + run: cargo build --verbose + - name: Run tests + run: cargo test --verbose diff --git a/README.md b/README.md index 826aa9b..c8c06e1 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,9 @@ [![paystack-rs on docs.rs](https://docs.rs/paystack-rs/badge.svg)](https://docs.rs/paystack-rs) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) ->*NB: a core rewrite of the project is being carried out, the goal is to make the crate easier to maintain, -improve the performance, abstract certain features to improve flexibility, and support for both blocking and non-blocking -operations.* +> _NB: a core rewrite of the project is being carried out, the goal is to make the crate easier to maintain, +> improve the performance, abstract certain features to improve flexibility, and support for both blocking and non-blocking +> operations._ Convenient Rust bindings and types for the [Paystack](https://paystack.com) HTTP API aiming to support the entire API surface. Not the case? Please open an issue. I update the definitions on a weekly basis. @@ -68,7 +68,7 @@ async fn main() -> Result<(), PaystackAPIError> { let api_key = env::var("PAYSTACK_API_KEY").unwrap(); let client = PaystackClient::new(api_key); - + let email = "email@example.com".to_string(); let amount ="10_000".to_string(); let body = TransactionRequestBuilder::default() @@ -94,8 +94,8 @@ async fn main() -> Result<(), PaystackAPIError> { ``` ### Examples -We provide some examples of use cases for the Paystack-rs crate. The examples are located in the [examples](examples) folder. +We provide some examples of use cases for the Paystack-rs crate. The examples are located in the [examples](examples) folder. ## Contributing @@ -105,12 +105,41 @@ We use Github actions to conduct CI/CD for the crate. It ensure that code is for as proper linting using `cargo clippy`, and finally run all the integration and unit test using `cargo test`. ### Crate module schematic diagram + A conceptual overview of the crate is illustrated below. This is to help improve the understanding of how the different parts of the crate interact with each other to work efficiently. The `PaystackClient` module is the central module of the crate and the best entry point to explore the different parts of the crate. -![Crate Schematic](docs/images/paystack-rs.png) - +```mermaid +--- +config: + layout: dagre + theme: default +--- +flowchart TD + subgraph subGraph0["HTTP Layer"] + HTTPClient["<Trait> HTTPClient"] + ReqwestClient["ReqwestClient"] + OtherClients["OtherClients"] + end + subgraph Core["Core"] + Models["Models"] + APIEndpoints["APIEndpoints"] + Macros["Macros"] + end + subgraph Types["Types"] + Response["Response"] + Request["Request"] + Error["Error"] + end + ReqwestClient --> HTTPClient + OtherClients --> HTTPClient + HTTPClient --> PaystackClient["PaystackClient"] + PaystackClient --> APIEndpoints + APIEndpoints --> Models + Macros --> Models & APIEndpoints + Models --> Response & Request & Error +``` ## License diff --git a/docs/images/paystack-rs.png b/docs/images/paystack-rs.png deleted file mode 100644 index bb20cee..0000000 Binary files a/docs/images/paystack-rs.png and /dev/null differ diff --git a/docs/uml/paystack-rs.drawio b/docs/uml/paystack-rs.drawio deleted file mode 100644 index ce84b80..0000000 --- a/docs/uml/paystack-rs.drawio +++ /dev/null @@ -1,72 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/client.rs b/src/client.rs index 80ed181..ca82acf 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,7 +1,10 @@ //! Client //! ========= //! This file contains the Paystack API client, and it associated endpoints. -use crate::{HttpClient, SubaccountEndpoints, TransactionEndpoints, TransactionSplitEndpoints}; +use crate::{ + HttpClient, SubaccountEndpoints, TerminalEndpoints, TransactionEndpoints, + TransactionSplitEndpoints, +}; use std::sync::Arc; /// This is the entry level struct for the paystack API. @@ -13,6 +16,8 @@ pub struct PaystackClient { pub transaction_split: TransactionSplitEndpoints, /// Subaccount API route pub subaccount: SubaccountEndpoints, + /// Terminal API route + pub terminal: TerminalEndpoints, } impl PaystackClient { @@ -23,6 +28,7 @@ impl PaystackClient { transaction: TransactionEndpoints::new(api_key.clone(), Arc::clone(&http)), transaction_split: TransactionSplitEndpoints::new(api_key.clone(), Arc::clone(&http)), subaccount: SubaccountEndpoints::new(api_key.clone(), Arc::clone(&http)), + terminal: TerminalEndpoints::new(api_key.clone(), Arc::clone(&http)), } } } diff --git a/src/endpoints/mod.rs b/src/endpoints/mod.rs index 568ab91..652fe1f 100644 --- a/src/endpoints/mod.rs +++ b/src/endpoints/mod.rs @@ -1,8 +1,10 @@ pub mod subaccount; +pub mod terminal; pub mod transaction; pub mod transaction_split; // public re-export pub use subaccount::*; +pub use terminal::*; pub use transaction::*; pub use transaction_split::*; diff --git a/src/endpoints/subaccount.rs b/src/endpoints/subaccount.rs index f98d7de..4a3c38e 100644 --- a/src/endpoints/subaccount.rs +++ b/src/endpoints/subaccount.rs @@ -18,7 +18,7 @@ pub struct SubaccountEndpoints { } impl SubaccountEndpoints { - /// Constructor for the Subaccount object + /// Constructor pub fn new(key: String, http: Arc) -> SubaccountEndpoints { let base_url = String::from("https://api.paystack.co/subaccount"); SubaccountEndpoints { diff --git a/src/endpoints/terminal.rs b/src/endpoints/terminal.rs new file mode 100644 index 0000000..4317be2 --- /dev/null +++ b/src/endpoints/terminal.rs @@ -0,0 +1,40 @@ +//! Terminal +//! ======== +//! The Terminal API allows you to build delightful in-person payment experiences. + +use std::sync::Arc; + +use crate::{EventRequest, HttpClient, PaystackResult, SendEventResponseData}; + +/// A struct to hold all the functions of the terminal API endpoint +#[derive(Debug, Clone)] +pub struct TerminalEndpoints { + key: String, + base_url: String, + http: Arc, +} + +impl TerminalEndpoints { + /// Constructor + pub fn new(key: String, http: Arc) -> TerminalEndpoints { + let base_url = String::from("https://api.paystack.co/terminal"); + TerminalEndpoints { + key, + base_url, + http, + } + } + + /// Send an event from your application to the Paystack Terminal + /// + /// Takes in the following: + /// - `terminal_id`: The ID of the Terminal the event should be sent to. + /// - `EventRequest`: A struct containing the information of the event to send to the terminal. It is created with the `EventRequestBuilder`. + pub fn send_event( + &self, + terminal_id: String, + event_request: EventRequest, + ) -> PaystackResult { + todo!() + } +} diff --git a/src/endpoints/transaction.rs b/src/endpoints/transaction.rs index b3ecac5..102606e 100644 --- a/src/endpoints/transaction.rs +++ b/src/endpoints/transaction.rs @@ -21,7 +21,7 @@ pub struct TransactionEndpoints { } impl TransactionEndpoints { - /// Constructor for the transaction object + /// Constructor pub fn new(key: String, http: Arc) -> TransactionEndpoints { let base_url = String::from("https://api.paystack.co/transaction"); TransactionEndpoints { diff --git a/src/endpoints/transaction_split.rs b/src/endpoints/transaction_split.rs index ce19082..4431ae4 100644 --- a/src/endpoints/transaction_split.rs +++ b/src/endpoints/transaction_split.rs @@ -18,7 +18,7 @@ pub struct TransactionSplitEndpoints { } impl TransactionSplitEndpoints { - /// Constructor for the transaction object + /// Constructor pub fn new(key: String, http: Arc) -> TransactionSplitEndpoints { let base_url = String::from("https://api.paystack.co/split"); TransactionSplitEndpoints { diff --git a/src/http/base.rs b/src/http/base.rs index ef266c2..a3655a0 100644 --- a/src/http/base.rs +++ b/src/http/base.rs @@ -13,7 +13,6 @@ pub type Query<'a> = Vec<(&'a str, &'a str)>; /// with their preferred HTTP client. /// To be as generic as possible, the U generic stands for the HTTP response. /// Ideally, it should be bounded to specific traits common in all response. -/// TODO: Bound the U generic to the appropriate traits. #[async_trait] pub trait HttpClient: Debug + Default + Clone + Send { diff --git a/src/models/mod.rs b/src/models/mod.rs index 96695a1..d8ec405 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -7,6 +7,7 @@ pub mod response; pub mod split; pub mod status; pub mod subaccount_model; +pub mod terminal_model; pub mod transaction_model; pub mod transaction_split_model; @@ -20,5 +21,6 @@ pub use response::*; pub use split::*; pub use status::*; pub use subaccount_model::*; +pub use terminal_model::*; pub use transaction_model::*; pub use transaction_split_model::*; diff --git a/src/models/terminal_model.rs b/src/models/terminal_model.rs new file mode 100644 index 0000000..7d17c7c --- /dev/null +++ b/src/models/terminal_model.rs @@ -0,0 +1,74 @@ +//! Terminal +//! ========== +//! This file contains the models and options for the Terminal endpoint of the Paystack + +use derive_builder::Builder; +use serde::{Deserialize, Serialize}; +use std::fmt; + +/// The request body to send an event from your application to the Paystack Terminal +#[derive(Debug, Clone, Builder, Serialize, Deserialize)] +pub struct EventRequest { + #[serde(rename = "type")] + pub event_type: EventType, + pub action: TerminalAction, + pub data: EventRequestData, +} + +/// The paramters needed to perform the specified action. +/// +/// For the invoice type, you need to pass the invoice id and offline reference: {id: invoice_id, reference: offline_reference}. +/// +/// For the transaction type, you can pass the transaction id: {id: transaction_id}, reference field can be `None` +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct EventRequestData { + pub id: String, + pub reference: Option, +} + +/// The type of event to push. +/// Paystack currently support `invoice` and `transaction` +#[derive(Debug, Serialize, Deserialize, Clone)] +pub enum EventType { + Invoice, + Transaction, +} + +/// The action the Terminal needs to perform. +/// +/// - For the `Invoice` type, the action can either be `Process` or `View`. +/// +/// - For the `Transaction` type, the action can either be `Process` or `Print`. +#[derive(Debug, Serialize, Deserialize, Clone)] +pub enum TerminalAction { + Process, + View, + Print, +} + +impl fmt::Display for TerminalAction { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + let action = match self { + TerminalAction::Process => "process", + TerminalAction::Print => "print", + TerminalAction::View => "view", + }; + write!(f, "{}", action) + } +} + +impl fmt::Display for EventType { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + let event = match self { + EventType::Invoice => "invoice", + EventType::Transaction => "transaction", + }; + write!(f, "{}", event) + } +} + +/// Contains response data for the send event route in the terminal endpoint. +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct SendEventResponseData { + pub id: String, +}