Telegram.Bot icon indicating copy to clipboard operation
Telegram.Bot copied to clipboard

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

Open AndreyPolovodov opened this issue 5 years ago • 14 comments

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.

AndreyPolovodov avatar Aug 31 '20 12:08 AndreyPolovodov

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.

karb0f0s avatar Aug 31 '20 13:08 karb0f0s

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?

AndreyPolovodov avatar Aug 31 '20 13:08 AndreyPolovodov

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.

tuscen avatar Aug 31 '20 14:08 tuscen

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".

AndreyPolovodov avatar Aug 31 '20 14:08 AndreyPolovodov

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.

tuscen avatar Aug 31 '20 14:08 tuscen

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 avatar Aug 31 '20 15:08 karb0f0s

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
}

karb0f0s avatar Aug 31 '20 15:08 karb0f0s

@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.

tuscen avatar Aug 31 '20 15:08 tuscen

@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.

AndreyPolovodov avatar Aug 31 '20 15:08 AndreyPolovodov

@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.

tuscen avatar Aug 31 '20 15:08 tuscen

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?

karb0f0s avatar Aug 31 '20 15:08 karb0f0s

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).

AndreyPolovodov avatar Aug 31 '20 15:08 AndreyPolovodov

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

karb0f0s avatar Sep 16 '20 11:09 karb0f0s

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

AndreyPolovodov avatar Sep 16 '20 11:09 AndreyPolovodov