Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Payments #657

Open
wants to merge 20 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions config/telegraph.php
Original file line number Diff line number Diff line change
Expand Up @@ -207,4 +207,8 @@
*/
'start_with' => ['/'],
],

'payments' => [
'provider_token' => env('TELEGRAPH_PAYMENT_PROVIDER_TOKEN', ''),
],
];
51 changes: 51 additions & 0 deletions docs/12.features/7.attachments.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,57 @@ and validation checks limits can be customized in [telegraph.php config](install

## Attachment types

### Invoice

Invoices can be sent through Telegraph `->invoice()` method:

```php
Telegraph::invoice('Invoice title')
->description('Invoice Description')
->currency('EUR') //Pass “XTR” for payments in Telegram Stars
->addItem('First Item Label', 10) //Must contain exactly one item for payments in Telegram Stars
->addItem('Second Item Label', 10)
->maxTip(70) //Not supported for payments in Telegram Stars
->suggestedTips([30,20])
->startParameter(10)
->image('Invoice Image Link', 20 , 20)
->needName() //Ignored for payments in Telegram Stars
->needPhoneNumber() //Ignored for payments in Telegram Stars
->needEmail() //Ignored for payments in Telegram Stars
->needShippingAddress() //Ignored for payments in Telegram Stars
->flexible() //Ignored for payments in Telegram Stars
->send();
```

A link for the invoice can be created through the `->link()` method

```php
Telegraph::invoice('Invoice title')
->description('Invoice Description')
->currency('EUR')
->addItem('Item Label', 10)
->link()
->send();
```

Payments require a provider token, pass an empty string (default) for payments in Telegram Stars.
To change it, you should specify your provider token in the `.env` file.

```php
TELEGRAPH_PAYMENT_PROVIDER_TOKEN = "provider token"
```

Alternatively you can set it through the `->providerData()` method

```php
Telegraph::invoice('Invoice title')
->description('Invoice Description')
->currency('EUR')
->addItem('Item Label', 10)
->providerData('provider token')
->send();
```

### Photos

Photos can be sent through Telegraph `->photo()` method:
Expand Down
30 changes: 27 additions & 3 deletions docs/12.features/9.dto.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ contains incoming data (a message or a callback query)
- `->id()` incoming _update_id_
- `->message()` (optional) an instance of [`Message`](#message)
- `->messageReaction()` (optional) an instance of [`Reaction`](#reaction)
- `->callbackQuery()` (optional) an instance of [`CallbackQuery`](#callback-query)
- `->callbackQuery()` (optional) an instance of [`CallbackQuery`](#callbackQuery)
- `->preCheckoutQuery()` (optional) an instance of [`PreCheckoutQuery`](#preCheckoutQuery)

## `Chat`

Expand Down Expand Up @@ -45,6 +46,8 @@ contains incoming data (a message or a callback query)
- `->sticker()` (optional) an instance of [`Sticker`](#sticker) holding data about the contained sticker
- `->venue()` (optional) an instance of [`Venue`](#venue) holding data about the contained sticker
- `->entities()` (optional) a collection of [`Entity`](#entity) holding data about the contained entity
- `->invoice()` (optional) an instance of [`Invoice`](#invoice) holding data about the contained invoice
- `->successfulPayment()` (optional) an instance of [`SuccessfulPayment`](#successfulPayment) holding data about the successful payment with information about the payment
- `->newChatMembers()` a collection of [`User`](#user) holding the list of users that joined the group/supergroup
- `->leftChatMember()` (optional) an instance of [`User`](#user) holding data about the user that left the group/supergroup
- `->webAppData()` (optional) incoming data from sendData method of telegram WebApp
Expand All @@ -56,10 +59,14 @@ contains incoming data (a message or a callback query)
## `CallbackQuery`

- `->id()` incoming _callback_query_id_
- `->from()` (optional) an instance of the [`User`](#user) that triggered the callback query
- `->from()` an instance of the [`User`](#user) that triggered the callback query
- `->message()` (optional) an instance of the [`Message`](#message) that triggered the callback query
- `->data()` an `Illuminate\Support\Collection` that holds the key/value pairs of the callback query data

## `PreCheckoutQuery`

ore information on the [payment](payment#preCheckoutQuery) page.

## `Reaction`

- `->id()` incoming _message_id_
Expand Down Expand Up @@ -87,6 +94,11 @@ contains incoming data (a message or a callback query)
- `->languageCode()` user's language code
- `->isPremium()` user's premium status

## `Invoice`

More information on the [payment](payment#invoice) page.


## `Audio`

- `->id()` file ID
Expand Down Expand Up @@ -274,4 +286,16 @@ represents a join request sent to a chat.
- `->userChatId()` identifier of a private chat with the user who sent the join request. This number may have more than 32 significant bits and some programming languages may have difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a 64-bit integer or double-precision float type are safe for storing this identifier. The bot can use this identifier for 5 minutes to send messages until the join request is processed, assuming no other administrator contacted the user.
- `->date()` date the request was sent in Unix time
- `->bio()` (optional) bio of the user
- `->inviteLink()` (optional) an instance of [`ChatInviteLink`](#chat-invite-link) that was used by the user to send the join request
- `->inviteLink()` (optional) an instance of [`ChatInviteLink`](#chatInviteLink) that was used by the user to send the join request

## `OrderInfo`

represents information about an order. More information on the [payment](payment#orderInfo) page.

## `ShippingAddress`

represents a shipping address. More information on the [payment](payment#shippingAddress) page.

## `SuccessfulPayment`

represents a successful payment. More information on the [payment](payment#successfulPayment) page.
171 changes: 171 additions & 0 deletions docs/12.features/payment.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
---
title: 'Payments'
---

## How it works

After creating a Bot you need to have a payment provider token, see https://core.telegram.org/bots/payments for more information about it.

Invoices are sent via the [`Invoice`](#invoice) method. The bot forms an invoice message with a description of the goods or service,
amount to be paid, and requested shipping info.

Once the payment is completed the Bot API sends an Update with the field [`preCheckoutQuery`](#precheckoutquery) to the bot that
contains all the available information about the order. Your bot must reply using answerPrecheckoutQuery through the Webhook Handler within
10 seconds after receiving this update or the transaction is canceled. Webhook Handler is already set to send it through the ```handlePreCheckoutQuery()``` method.

The bot may return an error if it can't process the order for any reason. We highly recommend specifying a reason for
failure to complete the order in human readable form (e.g. "Sorry, we're all out of rubber ducks! Would you be interested
in a cast iron bear instead?"). Telegram will display this reason to the user.

In case the bot confirms the order, Telegram requests the payment provider to complete the transaction.
If the payment information was entered correctly and the payment goes through, the API will send a receipt
message of the type ```successful_payment``` through the Webhook Handler method ```handleSuccessfulPayment()```.

See the [`example`](#example) below for further clarification


## Example

Example : System for creating payments and saving invoices

Let's create a new class that extends the ```WebhookHandler``` class

```php
class TestInvoicingHandler extends WebhookHandler
{

}
```

Let's create a button for the item for sale

```php
public function exampleButton(): void
{
$this->chat->message('Table')
->keyboard(fn(Keyboard $keyboard) => $keyboard->button('Buy')->action('buy')->param('item_id', 42));
}
```

We need to create a function that sends the invoice and , if necessary, stores it

```php
public function buy(int $item_id): void
{
$invoice = InvoiceModel::create([...]);

$this->chat->invoice('Attached is the invoice for your order')
->currency('EUR')
->addItem('Table', 100)
->payload($invoice->id)
->invoice();
}
```

Once the payment is completed, we can use the Webhook Handler for further operations

```php
protected function handleSuccessfulPayment(SuccessfulPayment $successfulPayment): void
{
//Example : check the invoice data
$invoice = Invoice::findOrFail($successfulPayment->invoicePayload());

if ($invoice->total() !== $successfulPayment->totalAmount()) {
//...errors
}
}
```


### Attachments

Invoices can be sent through Telegraph `->invoice()` method:

```php
Telegraph::invoice('Invoice title')
->description('Invoice Description')
->currency('EUR') //Pass “XTR” for payments in Telegram Stars
->addItem('First Item Label', 10) //Must contain exactly one item for payments in Telegram Stars
->addItem('Second Item Label', 10)
->maxTip(70) //Not supported for payments in Telegram Stars
->suggestedTips([30,20])
->startParameter(10)
->image('Invoice Image Link', 20 , 20)
->needName() //Ignored for payments in Telegram Stars
->needPhoneNumber() //Ignored for payments in Telegram Stars
->needEmail() //Ignored for payments in Telegram Stars
->needShippingAddress() //Ignored for payments in Telegram Stars
->flexible() //Ignored for payments in Telegram Stars
->send();
```

### Incoming Data

## `Invoice`

- `->title()` invoice title
- `->description()` invoice description
- `->startParameter()` unique bot deep-linking parameter that can be used to generate this invoice
- `->currency()` invoice currency
- `->totalAmount()` invoice total amount (integer, not float/double)


## `PreCheckoutQuery`

- `->id()` unique query identifier
- `->from()` an instance of the [dto](9.dto.md#user) that triggered the query
- `->currency()` three-letter ISO 4217 currency code, or “XTR” for payments in Telegram Stars
- `->totalAmount()` total price in the smallest units of the currency
- `->invoicePayload()` bot-specified invoice payload
- `->ShippingOptionId()` (optional) identifier of the shipping option chosen by the user
- `->orderInfo()` (optional) an instance of the [`OrderInfo`](#orderinfo) order information provided by the user

## `OrderInfo`

represents information about an order.

- `->name()` (optional) user name
- `->phoneNumber()` (optional) user's phone number
- `->email()` (optional) user email
- `->shippingAddress()` (optional) an instance of [`ShippingAddress`](#shippingAddress) user shipping address


## `ShippingAddress`

represents a shipping address.

- `->countryCode()` two-letter ISO 3166-1 alpha-2 country code
- `->state()` state, if applicable
- `->city()` city
- `->streetLine1()` first line for the address
- `->streetLine2()` second line for the address
- `->postCode()` address post code

## `PreCheckoutQuery`

- `->id()` unique query identifier
- `->from()` an instance of the [dto](9.dto.md#user) that triggered the query
- `->currency()` three-letter ISO 4217 currency code, or “XTR” for payments in Telegram Stars
- `->totalAmount()` total price in the smallest units of the currency
- `->invoicePayload()` bot-specified invoice payload
- `->ShippingOptionId()` (optional) identifier of the shipping option chosen by the user
- `->orderInfo()` (optional) an instance of the [`OrderInfo`](#orderinfo) order information provided by the user


## `SuccessfulPayment`

represents a successful payment.

- `->currency()` three-letter ISO 4217 currency code, or “XTR” for payments in Telegram Stars
- `->totalAmount()` total price in the smallest units of the currency
- `->invoicePayload()` bot-specified invoice payload
- `->subscriptionExpirationDate()` (optional) expiration date of the subscription, in Unix time; for recurring payments only
- `->isRecurring()` (optional) true, if the payment is a recurring payment for a subscription
- `->isFirstRecurring()` (optional) true, if the payment is the first payment for a subscription
- `->shippingOptionId()` (optional) identifier of the shipping option chosen by the user
- `->orderInfo()` (optional) order information provided by the user
- `->telegramPaymentChargeId()` telegram payment identifier
- `->providerPaymentChargeId()` provider payment identifier

> [!NOTE]
> If the buyer initiates a chargeback with the relevant payment provider following this transaction, the funds may be debited from your balance. This is outside of Telegram's control.
11 changes: 11 additions & 0 deletions docs/14.models/2.telegraph-chat.md
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,17 @@ $telegraphChat->setMenuButton()->webApp("Web App", "https://my-web.app")->send()

# `Attachments`

### `invoice()`

sends an invoice

```php
$telegraphChat->invoice('Invoice title')
->description('Invoice Description')
->currency('EUR')
->addItem('Item Label', 10)
->send();
```

### `document()`

Expand Down
1 change: 1 addition & 0 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
<php>
<server name="SANDOBOX_TELEGRAM_BOT_TOKEN" value=":fake_bot_token:"/>
<server name="SANDBOX_TELEGRAM_CHAT_ID" value=""/>
<server name="SANDOBOX_TELEGRAM_PAYMENT_PROVIDER_TOKEN" value=""/>
</php>
<source>
<include>
Expand Down
4 changes: 2 additions & 2 deletions src/Client/TelegraphResponse.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public function telegraphMessageId(): int|null
return (int) $this->json('result.message_id');
}

public function dump($key = null): static
public function dump(mixed $key = null): static
{
dump($this->json($key));

Expand All @@ -41,7 +41,7 @@ public function dump($key = null): static
/**
* @return never-returns
*/
public function dd($key = null): void
public function dd(mixed $key = null): void
{
dd($this->json($key));
}
Expand Down
8 changes: 8 additions & 0 deletions src/Concerns/CreatesScopedPayloads.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace DefStudio\Telegraph\Concerns;

use DefStudio\Telegraph\Payments\TelegraphInvoicePayload;
use DefStudio\Telegraph\ScopedPayloads\TelegraphPollPayload;
use DefStudio\Telegraph\ScopedPayloads\TelegraphQuizPayload;

Expand All @@ -20,4 +21,11 @@ public function quiz(string $question): TelegraphQuizPayload

return $quizPayload->quiz($question);
}

public function invoice(string $title): TelegraphInvoicePayload
{
$invoicePayload = TelegraphInvoicePayload::makeFrom($this);

return $invoicePayload->invoice($title);
}
}
14 changes: 14 additions & 0 deletions src/Concerns/InteractsWithWebhooks.php
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,18 @@ public function replyWebhook(int $callbackQueryId, string $message, bool $showAl

return $telegraph;
}

public function answerPreCheckoutQuery(int $preCheckoutQueryId, bool $result, ?string $errorMessage = null): Telegraph
{
$telegraph = clone $this;

$telegraph->endpoint = self::ENDPOINT_ANSWER_PRE_CHECKOUT_QUERY;
$telegraph->data = [
'pre_checkout_query_id' => $preCheckoutQueryId,
'ok' => $result,
'error_message' => $errorMessage,
];

return $telegraph;
}
}
Loading
Loading