【问题标题】:How do I make an M-Pesa Callback URL using Firebase Cloud Firestore?如何使用 Firebase Cloud Firestore 创建 M-Pesa 回调 URL?
【发布时间】:2021-03-24 20:52:07
【问题描述】:

我正在尝试制作一个应用程序,可以使用 Safaricom 的“Lipa Na M-Pesa”(肯尼亚的东西)向 PayBill 号码发送付款。调用是对 URL 的 POST 请求:

https://sandbox.safaricom.co.ke/mpesa/stkpush/v1/processrequest

带有标题:

{
        'Host': 'sandbox.safaricom.co.ke',
        'Authorization': 'Bearer ${await mpesaAccessToken}',
        'Content-Type': 'application/json',
      }

和身体:

{
        "BusinessShortCode": "$businessShortCode",
        "Password": "${generateLnmPassword(timeStamp)}",
        "Timestamp": "$timeStamp",
        "TransactionType": "CustomerPayBillOnline",
        "Amount": "10",
        "PartyA": "$userPhoneNumber",
        "PartyB": "$businessShortCode",
        "PhoneNumber": "$userPhoneNumber",
        "CallBackURL": "?????????????????????????????",
        "AccountReference": "account",
        "TransactionDesc": "test",
      }

我已收到访问令牌、生成密码并成功拨打电话,但 CallBackURL 除外……M-Pesa 文档这样描述他们的回调:

CallBackURL 这是您希望传递事务结果的端点。注册 URL API 回调的规则相同。

所有来自事务性请求的 API 回调都是 POST 请求,不要指望 GET 请求作为回调。此外,数据没有格式化为 application/x-www-form-urlencoded 格式,它是 application/json,所以不要指望您的语言中通常的 POST 字段/变量中的数据,直接从传入的输入中读取结果流。

(更多信息在这里,但您可能需要登录:https://developer.safaricom.co.ke/get-started 参见“Lipa na M-Pesa”)

我的应用托管在 Firebase Cloud Firestore 上。有什么方法可以与他们一起创建一个回调 URL,将他们的回调作为 Firestore 集合中的文档接收?...

或者这是不可能的,因为他们需要授权令牌和其他东西......而且我无法影响 M-Pesa 将发送的标头和正文?

(顺便说一句,我在 Flutter/Dart 中编码,所以请不要用 Javascript 或任何东西回答!我会一无所知...:p Flutter/Dart 或纯文本都可以。谢谢!)

【问题讨论】:

  • 你在用颤振吗?

标签: firebase google-cloud-firestore callbackurl mpesa


【解决方案1】:

有什么方法可以用它们创建一个回调 URL 将他们的回调作为 Firestore 集合中的文档接收?...

在 Firebase 生态系统中,最常见的方法是编写一个HTTPS Cloud Function,它将被 Safaricom 服务调用。

在 Cloud Function 中,您将能够根据 POST 请求的内容更新 Firestore 文档。

类似:

exports.safaricom = functions.https.onRequest((req, res) => {
    // Get the header and body through the req variable
    // See https://firebase.google.com/docs/functions/http-events#read_values_from_the_request

    return admin.firestore().collection('...').doc('...').update({ foo: bar })
        .then(() => {
            res.status(200).send("OK");
        })
        .catch(error => {
            // ...
            // See https://www.youtube.com/watch?v=7IkUgCLr5oA&t=1s&list=PLl-K7zZEsYLkPZHe41m4jfAxUi0JjLgSM&index=3
        })

});

我确实注意到您要求我们不要“用 Javascript 或任何东西回答”而是在 Flutter/Dart 中,但我认为您无法在 Flutter 中实现它:您需要在一个环境中实现这个 webhook您可以完全控制并公开 API 端点,例如您自己的服务器或云函数。

云函数乍一看似乎很复杂,但实现 HTTPS 云函数并没有那么复杂。我建议您阅读 Get Started documentation 并观看 Firebase video series 中关于“JavaScript Promises”的三个视频,如果遇到任何问题,请在 SO 上提出新问题。

【讨论】:

  • 哇!你让它听起来完全可行! :) 是的,我确实在某处读到 Cloud Functions 只接受 JavaScript……这就是我当时没有进一步研究的原因。好吧,如果需要的话,这可能是时候了。 :)
  • (我想给你的答案投票,但我没有足够的声誉。非常感谢,无论如何!)
  • @KarolinaHagegård 很高兴我能帮助你!你可以接受答案。
  • 对.....转到qn no。 2:我将如何接受答案?! ?我没有找到任何这样的功能......我找到了一个编辑你的答案的功能,这有点有趣......但不接受它。
  • 啊!现在找到那个小抽动标记......离散的小东西! ? 再次感谢。
【解决方案2】:

云函数不是基于 Dart 的。

参见下面的解决方案;

const functions = require("firebase-functions");
const admin = require("firebase-admin");
const parse = require("./parse");

admin.initializeApp();

exports.lmno_callback_url = functions.https.onRequest(async (req, res) => {
    const callbackData = req.body.Body.stkCallback;
    const parsedData = parse(callbackData);

    let lmnoResponse = admin.firestore().collection('lmno_responses').doc('/' + parsedData.checkoutRequestID + '/');
    let transaction = admin.firestore().collection('transactions').doc('/' + parsedData.checkoutRequestID + '/');
    let wallets = admin.firestore().collection('wallets');

    if ((await lmnoResponse.get()).exists) {
        await lmnoResponse.update(parsedData);
    } else {
        await lmnoResponse.set(parsedData);
    }
    if ((await transaction.get()).exists) {
        await transaction.update({
            'amount': parsedData.amount,
            'confirmed': true
        });
    } else {
        await transaction.set({
            'moneyType': 'money',
            'type': 'deposit',
            'amount': parsedData.amount,
            'confirmed': true
        });
    }
    let walletId = await transaction.get().then(value => value.data().toUserId);

    let wallet = wallets.doc('/' + walletId + '/');

    if ((await wallet.get()).exists) {
        let balance = await wallet.get().then(value => value.data().moneyBalance);
        await wallet.update({
            'moneyBalance': parsedData.amount + balance
        })
    } else {
        await wallet.set({
            'moneyBalance': parsedData.amount
        })
    }

    res.send("Completed");
});

解析函数。

const moment = require("moment");

function parse(responseData) {
    const parsedData = {};
    parsedData.merchantRequestID = responseData.MerchantRequestID;
    parsedData.checkoutRequestID = responseData.CheckoutRequestID;
    parsedData.resultDesc = responseData.ResultDesc;
    parsedData.resultCode = responseData.ResultCode;

    if (parsedData.resultCode === 0) {
        responseData.CallbackMetadata.Item.forEach(element => {
            switch (element.Name) {
                case "Amount":
                    parsedData.amount = element.Value;
                    break;
                case "MpesaReceiptNumber":
                    parsedData.mpesaReceiptNumber = element.Value;
                    break;
                case "TransactionDate":
                    parsedData.transactionDate = moment(
                        element.Value,
                        "YYYYMMDDhhmmss"
                    ).unix();
                    break;
                case "PhoneNumber":
                    parsedData.phoneNumber = element.Value;
                    break;
            }
        });
    }

    return parsedData;
}

module.exports = parse;

【讨论】:

    猜你喜欢
    • 2018-05-05
    • 2019-12-12
    • 2019-03-28
    • 1970-01-01
    • 2018-09-09
    • 1970-01-01
    • 2019-07-05
    • 2019-12-15
    • 2020-02-15
    相关资源
    最近更新 更多