TelegramBots / Telegram.Bot

.NET Client for Telegram Bot API
https://telegrambots.github.io/book
MIT License
3.17k stars 684 forks source link

It's not possible to pass the string into providerData parameter of SendInvoiceAsync and get valid json subtree of "providerData" node as a result #925

Closed AndreyPolovodov closed 2 months ago

AndreyPolovodov commented 4 years ago

Here is a sample of result Im trying to get... image There is a problem with quotes escaping - I don't see the way how to pass a string into current version of SendInvoiceAsync to get valid json in request to telegram api. If just I don't see the way - please, let me know... Thank you.

karb0f0s commented 4 years ago

Please provide minimal reproducible example of what you expect to achieve there. For further discussion I suggest you to join our telegram group https://t.me/joinchat/B35YY0QbLfd034CFnvCtCA.

AndreyPolovodov commented 4 years ago

I want to get valid json as you can see on screen (this is provider requirement). They don't want a "string", they want JSON-node.

So, here is whay they want: image

And here is what I can do now with string providerData (i just put JObject.ToString() result into providerData): image

As you can see, there is no JSON-node, but just a string with escaped quotes.

The question is - how to get what they want using SendInvoiceAsync? I don't see the way.

await _botClient.SendInvoiceAsync(identifier, "",
                                            "desc", "payload", paymentToken, "start_parameter", "RUB",
                                             prices,
                                            /* WHAT TO PLACE HERE TO GET IT AS A PART OF VALID JSON */,

So, is it enought as reproducible example or not?

tuscen commented 4 years ago

Who are “they”? Bot API clearly states that provider data is a json encoded string, there’s nothing indicating that it should be a valid json node without any particular schema.

AndreyPolovodov commented 4 years ago

Yandex. See here for example, if you are interested in. I captured my first screenshot from that page.

Possibly you are right but Im not sure actually.

Here is quote from a telegram bot api:

A JSON-serialized data about the invoice, which will be shared with the payment provider. A detailed description of required fields should be provided by the payment provider.

So, there is a "data" and "fields". But at the same time there is type of parameter "String".

tuscen commented 4 years ago

It probably means that you should serialize required provider data as a json string and pass it as json as is. It’s either Telegram or the provider job to deserialize it back.

karb0f0s commented 4 years ago

According to the official documentation for sendInvoice provider_data parameter provider_data is a JSON-serialized data about the invoice, which will be shared with the payment provider. A detailed description of required fields should be provided by the payment provider. So this property is provider-specific I i don't see a way for us to provide a convenient way of serializing this data in our library. You have to serialize this data yourself and specify it as a parameter for SendInvoiceAsync. In your case it could be something like this:

using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using System;

var receipt = new Receipt()
{
    Items = new Item[] { new Item() { Description = "Product", Quantity = 1.0M } }
};

var providerData = JsonConvert.SerializeObject(receipt);
Console.WriteLine(providerData);

[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class Item
{
    [JsonProperty(Required = Required.Always)]
    public string Description { get; set; }
    [JsonProperty(Required = Required.Always)]
    public decimal Quantity { get; set; }
}

[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class Receipt
{
    [JsonProperty(Required = Required.Always)]
    public Item[] Items { get; set; }
}

With a resulting string:

{"items":[{"description":"Product","quantity":1.0}]}
karb0f0s commented 4 years ago

And a complete request would look like this:

using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using System;
using Telegram.Bot.Requests;
using Telegram.Bot.Types.Payments;

var receipt = new Receipt()
{
    Items = new Item[] { new Item() { Description = "Product", Quantity = 1.0M } }
};

LabeledPrice[] productPrices =
{
    new LabeledPrice(label: "PART_OF_PRODUCT_PRICE_1", amount: 150),
    new LabeledPrice(label: "PART_OF_PRODUCT_PRICE_2", amount: 2029),
};

var invoice = new Invoice
{
    Title = "PRODUCT_TITLE",
    Currency = "CAD",
    StartParameter = "start_param",
    TotalAmount = 1,
    Description = "PRODUCT_DESCRIPTION",
};

var sendInvoiceRequest = new SendInvoiceRequest(
    chatId: 1,
    title: invoice.Title,
    description: invoice.Description,
    payload: "payload",
    providerToken: "providerToken",
    startParameter: invoice.StartParameter,
    currency: invoice.Currency,
    prices: productPrices
    )
{
    PhotoUrl = "url",
    PhotoWidth = 600,
    PhotoHeight = 400,
    NeedShippingAddress = true,
    IsFlexible = true,
    NeedName = true,
    NeedEmail = true,
    NeedPhoneNumber = true,
    SendEmailToProvider = true,
    SendPhoneNumberToProvider = true,
    ProviderData = providerData
};

var invoiceRequest = JsonConvert.SerializeObject(sendInvoiceRequest);
Console.WriteLine(invoiceRequest);

[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class Item
{
    [JsonProperty(Required = Required.Always)]
    public string Description { get; set; }
    [JsonProperty(Required = Required.Always)]
    public decimal Quantity { get; set; }
}

[JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
public class Receipt
{
    [JsonProperty(Required = Required.Always)]
    public Item[] Items { get; set; }
}

Which gives us a valid JSON request:

{
    "chat_id": 1,
    "title": "PRODUCT_TITLE",
    "description": "PRODUCT_DESCRIPTION",
    "payload": "payload",
    "provider_token": "providerToken",
    "start_parameter": "start_param",
    "currency": "CAD",
    "prices": [
        {
            "label": "PART_OF_PRODUCT_PRICE_1",
            "amount": 150
        },
        {
            "label": "PART_OF_PRODUCT_PRICE_2",
            "amount": 2029
        }
    ],
    "provider_data": "{\"items\":[{\"description\":\"Product\",\"quantity\":1.0}]}",
    "photo_url": "url",
    "photo_width": 600,
    "photo_height": 400,
    "need_name": true,
    "need_phone_number": true,
    "need_email": true,
    "need_shipping_address": true,
    "send_phone_number_to_provider": true,
    "send_email_to_provider": true,
    "is_flexible": true
}
tuscen commented 4 years ago

@karb0f0s There’s a way to make the library do the conversion for you (making a breaking change) but it will require SendInvoiceAsync method and SendInvoiceRequest to become generic or provider_data to have object type. It will also require a custom json converter which will make future possibility of STJ support without pulling in Newtonsoft impossible. I don’t think it’s worth it.

AndreyPolovodov commented 4 years ago

@karb0f0s Thank you for a sample... Anyway, this string will give us "provider_data" with just a string value, not a JSON-node. Possible it's ok but it not looks like in yandex documentation. So, I will ask provider (yandex) support about that. There is still exists the possibility of error in their documentation.

@tuscen yes, that what I was looking for initially... object or JObject.

tuscen commented 4 years ago

@AndreyPolovodov it may be that yandex docs aren't correct regardless this particular case. Try deserializing all the required data as json object and test it with a test token. It should work.

karb0f0s commented 4 years ago

I see your point. Same example here https://habr.com/ru/company/yamoney/blog/351766/. It looks like provider_data should be a valid JSON node. Can you confirm, that serialized JSON string doesn't work as expected?

AndreyPolovodov commented 4 years ago

Can you confirm, that serialized JSON string doesn't work as expected?

I just trying to understand what is wrong with payments in my bot and have just found this one. Possibly it's not the only one, so, I can't confirm right now - but I will do it later (and will ask yandex to be sure).

karb0f0s commented 4 years ago

@AndreyPolovodov Hi, were you able to get reply from Yandex and resolve your issue?

AndreyPolovodov commented 4 years ago

Hi, @karb0f0s, sorry, not yet, but I remeber about and will give feedback a bit later.

wiz0u commented 2 months ago

Closing this.

Github Issues are for issues within our library code, not for helping you solve your client's requirements. For Bot API, ProviderData is a string. It's up to you to serialize your JSON to a string.