26 个版本 (2 个稳定版)

1.1.0 2023年11月14日
0.4.2 2021年6月22日
0.3.3 2021年3月29日
0.2.6 2020年11月22日
0.1.2 2020年7月26日

财务 类别中排名 #8

MIT 许可证

145KB
2.5K SLoC

mpesa-rust

Rust

关于

一个非官方的 Rust 封装,用于访问 M-Pesa 服务,基于 Safaricom API

安装

Cargo.toml

[dependencies]
mpesa = { version = "1.1.0" }

可选地,您可以通过禁用默认功能来禁用整个 MPESA API 套件,以有条件地选择以下之一

  • b2b
  • b2c
  • account_balance
  • c2b_register
  • c2b_simulate
  • express_request
  • transaction_reversal
  • transaction_status
  • bill_manager
  • dynamic_qr

示例

[dependencies]
mpesa = { git = "1.1.0", default_features = false, features = ["b2b", "express_request"] }

在您的 lib 或二进制 crate 中

use mpesa::Mpesa;

使用方法

创建一个 Mpesa 客户端

您首先需要创建一个 Mpesa 实例(客户端)。您需要提供 CLIENT_KEYCLIENT_SECRET。您可以从 这里 获取 Safaricom 沙盒环境的这些凭证。需要注意的是,这些凭证仅在沙盒环境中有效。要上线并获得生产密钥,请阅读文档 此处

以下是如何实例化 Mpesa 的方法

use mpesa::{Mpesa, Environment};

let client = Mpesa::new(
      env!("CLIENT_KEY"),
      env!("CLIENT_SECRET"),
      Environment::Sandbox,
);

assert!(client.is_connected().await)

由于 Environment 枚举实现了 FromStrTryFrom 对于 String&str 类型,您可以使用 Environment::from_strEnvironment::try_from 来创建一个 Environment 类型。如果环境值存储在 .env 或其他配置文件中,这是理想的

use mpesa::{Mpesa, Environment};
use std::str::FromStr;
use std::convert::TryFrom;

let client0 = Mpesa::new(
      env!("CLIENT_KEY"),
      env!("CLIENT_SECRET"),
      Environment::from_str("sandbox")? // "Sandbox" and "SANDBOX" also valid
);
assert!(client0.is_connected().await)

let client1 = Mpesa::new(
      env!("CLIENT_KEY"),
      env!("CLIENT_SECRET"),
      Environment::try_from("production")? // "Production" and "PRODUCTION" also valid
);
assert!(client1.is_connected().await)

Mpesa 结构体的 environment 参数是对任何实现了 ApiEnvironment 特性的类型进行泛型。此特性期望为给定类型实现以下方法

pub trait ApiEnvironment {
    fn base_url(&self) -> &str;
    fn get_certificate(&self) -> &str;
}

此特性允许您创建自己的类型并将其传递给environment参数。有了这个特性,您就可以通过从base_url方法返回模拟服务器URI来模拟http请求(用于测试目的),并且可以使用自己的证书来签名选择MPESA API的请求,通过提供自己的get_certificate实现。

请参阅下面的示例(以及这里查看如何为Environment枚举实现此特性)

use mpesa::{Mpesa, ApiEnvironment};
use std::str::FromStr;
use std::convert::TryFrom;

pub struct MyCustomEnvironment;

impl ApiEnvironment for MyCustomEnvironment {
    fn base_url(&self) -> &str {
        // your base url here
        "https://your_base_url.com"
    }

    fn get_certificate(&self) -> &str {
        // your certificate here
        r#"..."#
    }
}

let client: Mpesa<MyCustomEnvironment> = Mpesa::new(
    env!("CLIENT_KEY"),
    env!("CLIENT_SECRET"),
    MyCustomEnvironment // ✔ valid
);

//...

如果您打算在生产中使用,您需要在创建客户端后调用Mpesa中的set_initiator_password方法。在这里,您提供发起者密码,该密码将覆盖在沙箱中使用的默认密码"Safcom496!"

use mpesa::Mpesa;

let client = Mpesa::new(
      env!("CLIENT_KEY"),
      env!("CLIENT_SECRET"),
      Environment::Sandbox,
);

client.set_initiator_password("new_password");

assert!(client.is_connected().await)

服务

以下服务目前可通过Mpesa客户端作为返回构建器的函数提供:

  • B2C
let response = client
    .b2c("testapi496")
    .party_a("600496")
    .party_b("254708374149")
    .result_url("https://testdomain.com/ok")
    .timeout_url("https://testdomain.com/err")
    .amount(1000)
    .send()
    .await;
assert!(response.is_ok())
  • B2B
let response = client
    .b2b("testapi496")
    .party_a("600496")
    .party_b("254708374149")
    .result_url("https://testdomain.com/ok")
    .timeout_url("https://testdomain.com/err")
    .account_ref("254708374149")
    .amount(1000)
    .send()
    .await;
assert!(response.is_ok())
  • C2B 注册
let response = client
    .c2b_register()
    .short_code("600496")
    .confirmation_url("https://testdomain.com/true")
    .validation_url("https://testdomain.com/valid")
    .send()
    .await;
assert!(response.is_ok())
  • C2B 模拟
let response = client
    .c2b_simulate()
    .short_code("600496")
    .msisdn("254700000000")
    .amount(1000)
    .send()
    .await;
assert!(response.is_ok())
  • 账户余额
let response = client
    .account_balance("testapi496")
    .result_url("https://testdomain.com/ok")
    .timeout_url("https://testdomain.com/err")
    .party_a("600496")
    .send()
    .await;
assert!(response.is_ok())
  • Mpesa Express 请求 / STK 推送 / Lipa na M-PESA 线上
let response = client
    .express_request("174379")
    .phone_number("254708374149")
    .amount(500)
    .callback_url("https://test.example.com/api")
    .send()
    .await;
assert!(response.is_ok())
  • 交易撤销
let response = client
    .transaction_reversal("testapi496")
    .result_url("https://testdomain.com/ok")
    .timeout_url("https://testdomain.com/err")
    .transaction_id("OEI2AK4Q16")
    .receiver_identifier_type(IdentifierTypes::ShortCode)
    .amount(100.0)
    .receiver_party("600111")
    .send()
    .await;
assert!(response.is_ok())
  • 交易状态
let response = client
    .transaction_status("testapi496")
    .result_url("https://testdomain.com/ok")
    .timeout_url("https://testdomain.com/err")
    .transaction_id("OEI2AK4Q16")
    .identifier_type(IdentifierTypes::ShortCode)
    .party_a("600111")
    .remarks("status")
    .occasion("work")
    .send()
    .await;
assert!(response.is_ok())
  • 账单管理器上线
let response = client
    .onboard()
    .callback_url("https://testdomain.com/true")
    .email("[email protected]")
    .logo("https://file.domain/file.png")
    .official_contact("0712345678")
    .send_reminders(SendRemindersTypes::Enable)
    .short_code("600496")
    .send()
    .await;
assert!(response.is_ok())
  • 账单管理器上线修改
let response = client
    .onboard_modify()
    .callback_url("https://testdomain.com/true")
    .email("[email protected]")
    .short_code("600496")
    .send()
    .await;
assert!(response.is_ok())
  • 账单管理器批量发票
let response = client
    .bulk_invoice()
    .invoices(vec![
        Invoice {
            amount: 1000.0,
            account_reference: "John Doe",
            billed_full_name: "John Doe",
            billed_period: "August 2021",
            billed_phone_number: "0712345678",
            due_date: Utc::now(),
            external_reference: "INV2345",
            invoice_items: Some(
                vec![InvoiceItem {amount: 1000.0, item_name: "An item"}]
            ),
            invoice_name: "Invoice 001"
        }
    ])
    .send()
    .await;
assert!(response.is_ok())
  • 账单管理器单张发票
let response = client
    .single_invoice()
    .amount(1000.0)
    .account_reference("John Doe")
    .billed_full_name("John Doe")
    .billed_period("August 2021")
    .billed_phone_number("0712345678")
    .due_date(Utc::now())
    .external_reference("INV2345")
    .invoice_items(vec![
        InvoiceItem {amount: 1000.0, item_name: "An item"}
    ])
    .invoice_name("Invoice 001")
    .send()
    .await;
assert!(response.is_ok())
  • 账单管理器对账
let response = client
    .reconciliation()
    .account_reference("John Doe")
    .external_reference("INV2345")
    .full_name("John Doe")
    .invoice_name("Invoice 001")
    .paid_amount(1000.0)
    .payment_date(Utc::now())
    .phone_number("0712345678")
    .transaction_id("TRANSACTION_ID")
    .send()
    .await;
assert!(response.is_ok())
  • 账单管理器取消发票
let response = client
    .cancel_invoice()
    .external_references(vec!["9KLSS011"])
    .send()
    .await;
assert!(response.is_ok())
  • 动态二维码
let response = client
    .dynamic_qr_code()
    .amount(1000)
    .ref_no("John Doe")
    .size("300")
    .merchant_name("John Doe")
    .credit_party_identifier("600496")
    .try_transaction_type("bg")
    .unwrap()
    .build()
    .unwrap()
    .send()
    .await;
assert!(response.is_ok())

将逐步添加更多功能,欢迎提交拉取请求

作者

Collins Muriuki

贡献

欢迎贡献、问题和特性请求!
请随意查看问题页面。您还可以查看贡献指南

版权 © 2023 Collins Muriuki
此项目遵循MIT许可证。

依赖项

~15–29MB
~453K SLoC