草庐IT

ios - 如何向 Firebase 函数发送 post 请求?

coder 2023-09-16 原文

我创建了一个 Firebase 函数来通过 PayPal 向司机付款。

来自 firebase 函数的 index.js

'use strict';
const functions = require('firebase-functions');
const paypal = require('paypal-rest-sdk');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);

paypal.configure({
    mode: 'sandbox',
    client_id: functions.config().paypal.client_id,
    client_secret: functions.config().paypal.client_secret
})

exports.newRequest = functions.database.ref('/history/{pushId}').onCreate((snapshot, context) => {
    var requestSnapshot = snapshot.val();
    var price  = snapshot.child('price').val();
    var pushId = context.params.pushId;

    return snapshot.ref.parent.child(pushId).child('price').set(price);
 });


function getPayoutsPending(uid) {
    return admin.database().ref('Users/Drivers/' + uid + '/history').once('value').then((snap) => {
        if(snap === null){
            throw new Error("profile doesn't exist");
        }
        var array = [];
        if(snap.hasChildren()){
            snap.forEach(element => {
                if (element.val() === true) {
                    array.push(element.key);
                }
            });
        }
        return array;
    }).catch((error) => {
        return console.error(error);
    });
}

function getPayoutsAmount(array) {
    return admin.database().ref('history').once('value').then((snap) => {
        var value = 0.0;
        if(snap.hasChildren()){
            snap.forEach(element => {
                if(array.indexOf(element.key) > -1) {
                        if(element.child('price').val() !== null){
                            value += element.child('price').val();
                        }
                }
            });
            return value;
        }
        return value;
    }).catch((error) => {
        return console.error(error);
    });
}

function updatePaymentsPending(uid, paymentId) {
    return admin.database().ref('Users/Drivers/' + uid + '/history').once('value').then((snap) => {
        if(snap === null){
            throw new Error("profile doesn't exist");
        }

        if(snap.hasChildren()){
            snap.forEach(element => {
                if(element.val() === true) {
                    admin.database().ref('Users/Drivers/' + uid + '/history/' + element.key).set( {
                        timestamp: admin.database.ServerValue.TIMESTAMP,
                        paymentId: paymentId
                    });
                    admin.database().ref('history/' + element.key + '/driverPaidOut').set(true);
                }
            });
        }
        return null;
    }).catch((error) => {
        return console.error(error);
    });
}

exports.payout = functions.https.onRequest((request, response) => {
    return getPayoutsPending(request.body.uid)
        .then(array => getPayoutsAmount(array))
        .then(value => {
            var valueTrunc = parseFloat(Math.round((value * 0.75) * 100) / 100).toFixed(2);
            const sender_batch_id = Math.random().toString(36).substring(9);
            const sync_mode = 'false';
            const payReq = JSON.stringify({
                sender_batch_header: {
                    sender_batch_id: sender_batch_id,
                    email_subject: "You have a payment"
                },
                items: [
                    {
                        recipient_type: "EMAIL",
                        amount: {
                            value: valueTrunc,
                            currency: "CAD"
                        },
                        receiver: request.body.email,
                        note: "Thank you.",
                        sender_item_id: "Payment"
                    }
                ]
            });

            return paypal.payout.create(payReq, sync_mode, (error, payout) => {
                if (error) {
                    console.warn(error.response);
                    response.status('500').end();
                    throw error;
                }
                console.info("payout created");
                console.info(payout);
                return updatePaymentsPending(request.body.uid, sender_batch_id)
            });
        }).then(() => {
            response.status('200').end();
            return null;
        }).catch(error => {
            console.error(error);
        });
});

我在 Android 中使用的代码工作正常,但我在 iOS 中使用的代码无法正常工作。

IOS 中的代码,根据 index.js 文件,将司机的电子邮件放入文本字​​段并按下支付按钮后,它执行以下操作:

  • 它为用户/司机和用户/骑手添加支付 ID 和时间戳
  • 它将“driverPaidOut = true”添加到历史数据库
  • 它在 firebase 函数中创建并记录“支付已创建”

但它没有做的是将它发送到 PayPal 并创建一个通知到司机的电子邮件。

如您所见,index.js 文件需要两个变量:驱动程序的“uid”和“email”

有效的 Android 代码:

final OkHttpClient client = new OkHttpClient();

    // in json - we need variables for the hardcoded uid and Email
    JSONObject postData = new JSONObject();

    try {
        postData.put("uid", FirebaseAuth.getInstance().getCurrentUser().getUid());
        postData.put("email", mPayoutEmail.getText().toString());

    } catch (JSONException e) {
        e.printStackTrace();
    }

    // Request body ...
    RequestBody body = RequestBody.create(MEDIA_TYPE, postData.toString());

    // Build Request ...
    final Request request = new Request.Builder()
            .url("https://us-central1-ryyde-sj.cloudfunctions.net/payout")
            .post(body)
            .addHeader("Content-Type", "application/json")
            .addHeader("cache-control", "no-cache")
            .addHeader("Authorization", "Your Token")
            .build();

    client.newCall(request).enqueue(new Callback() {
        @Override
        public void onFailure(Call call, IOException e) {
            // something went wrong right off the bat
            progress.dismiss();
        }

        @Override
        public void onResponse(Call call, Response response) throws IOException {
            // response successful ....
            // refers to response.status('200') or ('500')
            int responseCode = response.code();
            if (response.isSuccessful()) {
                switch(responseCode) {
                    case 200:
                        Snackbar.make(findViewById(R.id.layout),
                                "Payout Successful!", Snackbar.LENGTH_LONG)
                                .show();
                        break;

                    case 500:
                        Snackbar.make(findViewById(R.id.layout),
                                "Error: no payout available", Snackbar
                                        .LENGTH_LONG).show();
                        break;

                    default:
                        Snackbar.make(findViewById(R.id.layout),
                                "Error: couldn't complete the transaction",
                                Snackbar.LENGTH_LONG).show();
                        break;
                }

            } else {
                Snackbar.make(findViewById(R.id.layout),
                        "Error: couldn't complete the transaction",
                        Snackbar.LENGTH_LONG).show();
            }

            progress.dismiss();
        }
    });

iOS 代码,无法运行:

let email = txtPayoutEmail.text!
    let userId = self.uid!

    let parameters = "uid=\(userId)&email=\(email)"
    let url = "https://us-central1-ryyde-sj.cloudfunctions.net/payout"
    var request = URLRequest(url: URL(string: url)!)

    request.setValue("application/json", forHTTPHeaderField: "Content-Type")
    request.setValue("Your Token", forHTTPHeaderField: "Authorization")
    request.setValue("no-cache", forHTTPHeaderField: "cache-control")

    request.httpMethod = "POST"
    print("URL : \(request)")

    request.httpBody = parameters.data(using: .utf8)

    let task = URLSession.shared.dataTask(with: request) { data, response, error in
        guard let data = data, error == nil else { return }

        if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode != 200 { // check for http errors
            print("statusCode should be 200, but is \(httpStatus.statusCode)")
        }

        do {
            let response = try JSONSerialization.jsonObject(with: data, options: []) as? [String:AnyObject]
            print(response!)

        } catch let error as NSError {
            print(error)
        }
    }

    task.resume()

我已经在网上尝试了不同的(几乎是网络上的每一个)示例,无论是否是 Alamofire,但似乎没有任何效果。

编辑 - Sahil Manchanda 的回复

获取httpResponse:

Optional(<NSHTTPURLResponse: 0x600000132620> { URL: https://us-central1-ryyde-sj.cloudfunctions.net/payout } { Status Code: 200, Headers {
"Content-Length" =     (
    0
);
"Content-Type" =     (
    "text/html"
);
Date =     (
    "Mon, 17 Dec 2018 07:44:57 GMT"
);
Server =     (
    "Google Frontend"
);
"alt-svc" =     (
    "quic=\":443\"; ma=2592000; v=\"44,43,39,35\""
);
"function-execution-id" =     (
    wspeunpt1umy
);
"x-cloud-trace-context" =     (
    "a2444e17fb874853489b56684de9fe10;o=1"
);
"x-powered-by" =     (
    Express
);
} })

编辑 #2 - 仍然无效

Sahil Manchanda 非常有帮助,但不是 Firebase 后端方面的专家。

在代码中做了一些修改后,调试了 index.js,由于某种原因,它在服务器端无法正常工作。

我的新代码:

let email = txtPayoutEmail.text!

    let headers = [
        "Content-Type": "application/json",
        "cache-control": "no-cache",
        "Authorization" : "Bearer \(token)"
    ]

    let body = ["uid": uid, "email": email]

    let postData = try! JSONSerialization.data(withJSONObject: body, options: [])


    let request = NSMutableURLRequest(url: NSURL(string: "https://us-central1-ryyde-sj.cloudfunctions.net/payout")! as URL,  cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0)

    request.httpMethod = "POST"
    request.allHTTPHeaderFields = headers
    request.httpBody = postData as Data

    let session = URLSession.shared
    let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
        if (error != nil) {
            print(error!)
        } else {
            let httpResponse = response as? HTTPURLResponse
            print(httpResponse!)
        }
    })

    dataTask.resume()

当运行应用程序时,这是在 Firebase 函数中看到的,看起来不错,但没有像在 Android 应用程序中那样访问 PayPal?

最佳答案

您缺少的主要内容是 iOS 代码中的 JSON 字符串。在 Android 中,您使用的是 JSONObject,然后将其转换为 String。而在您的 iOS 中,缺少此转换。试试下面的代码。看看它是否工作。我做了一些调整,应该可以满足您的需要。

import Foundation

let headers = [
  "Content-Type": "application/json",
  "Cache-Control": "no-cache",
  "Authorization" : "your token"
]
let parameters = [
  "email": "abc@abc.com",
  "uid": "12"
] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://us-central1-ryyde-sj.cloudfunctions.net/payout")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()

关于ios - 如何向 Firebase 函数发送 post 请求?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53810050/

有关ios - 如何向 Firebase 函数发送 post 请求?的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  3. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  4. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

    给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

  5. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  6. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  7. ruby - 如何指定 Rack 处理程序 - 2

    Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

  8. ruby - 如何每月在 Heroku 运行一次 Scheduler 插件? - 2

    在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/

  9. ruby-on-rails - 如何从 format.xml 中删除 <hash></hash> - 2

    我有一个对象has_many应呈现为xml的子对象。这不是问题。我的问题是我创建了一个Hash包含此数据,就像解析器需要它一样。但是rails自动将整个文件包含在.........我需要摆脱type="array"和我该如何处理?我没有在文档中找到任何内容。 最佳答案 我遇到了同样的问题;这是我的XML:我在用这个:entries.to_xml将散列数据转换为XML,但这会将条目的数据包装到中所以我修改了:entries.to_xml(root:"Contacts")但这仍然将转换后的XML包装在“联系人”中,将我的XML代码修改为

  10. ruby - 如何使用文字标量样式在 YAML 中转储字符串? - 2

    我有一大串格式化数据(例如JSON),我想使用Psychinruby​​同时保留格式转储到YAML。基本上,我希望JSON使用literalstyle出现在YAML中:---json:|{"page":1,"results":["item","another"],"total_pages":0}但是,当我使用YAML.dump时,它不使用文字样式。我得到这样的东西:---json:!"{\n\"page\":1,\n\"results\":[\n\"item\",\"another\"\n],\n\"total_pages\":0\n}\n"我如何告诉Psych以想要的样式转储标量?解

随机推荐