import BigInt from 'big-integer'; import { Api as GramJs } from '../../../lib/gramjs'; import type { ApiChat, ApiInputStorePaymentPurpose, ApiPeer, ApiRequestInputInvoice, ApiRequestInputSavedStarGift, ApiStarGiftRegular, ApiThemeParameters, } from '../../types'; import { DEBUG } from '../../../config'; import { buildApiSavedStarGift, buildApiStarGift, buildApiStarGiftAttribute, } from '../apiBuilders/gifts'; import { buildApiBoost, buildApiBoostsStatus, buildApiCheckedGiftCode, buildApiGiveawayInfo, buildApiMyBoost, buildApiPaymentForm, buildApiPremiumGiftCodeOption, buildApiPremiumPromo, buildApiReceipt, buildApiStarsAmount, buildApiStarsGiftOptions, buildApiStarsGiveawayOptions, buildApiStarsSubscription, buildApiStarsTransaction, buildApiStarTopupOption, buildShippingOptions, } from '../apiBuilders/payments'; import { buildApiPeerId } from '../apiBuilders/peers'; import { buildInputInvoice, buildInputPeer, buildInputSavedStarGift, buildInputStorePaymentPurpose, buildInputThemeParams, buildShippingInfo, } from '../gramjsBuilders'; import { deserializeBytes, serializeBytes, } from '../helpers'; import localDb from '../localDb'; import { sendApiUpdate } from '../updates/apiUpdateEmitter'; import { handleGramJsUpdate, invokeRequest } from './client'; import { getTemporaryPaymentPassword } from './twoFaSettings'; export async function validateRequestedInfo({ inputInvoice, requestInfo, shouldSave, }: { inputInvoice: ApiRequestInputInvoice; requestInfo: GramJs.TypePaymentRequestedInfo; shouldSave?: boolean; }): Promise<{ id: string; shippingOptions: any; } | undefined> { const result = await invokeRequest(new GramJs.payments.ValidateRequestedInfo({ invoice: buildInputInvoice(inputInvoice), save: shouldSave || undefined, info: buildShippingInfo(requestInfo), })); if (!result) { return undefined; } const { id, shippingOptions } = result; if (!id) { return undefined; } return { id, shippingOptions: buildShippingOptions(shippingOptions), }; } export async function sendPaymentForm({ inputInvoice, formId, requestedInfoId, shippingOptionId, credentials, savedCredentialId, temporaryPassword, tipAmount, }: { inputInvoice: ApiRequestInputInvoice; formId: string; credentials: any; requestedInfoId?: string; shippingOptionId?: string; savedCredentialId?: string; temporaryPassword?: string; tipAmount?: number; }) { const inputCredentials = temporaryPassword && savedCredentialId ? new GramJs.InputPaymentCredentialsSaved({ id: savedCredentialId, tmpPassword: deserializeBytes(temporaryPassword), }) : new GramJs.InputPaymentCredentials({ save: credentials.save, data: new GramJs.DataJSON({ data: JSON.stringify(credentials.data) }), }); const result = await invokeRequest(new GramJs.payments.SendPaymentForm({ formId: BigInt(formId), invoice: buildInputInvoice(inputInvoice), requestedInfoId, shippingOptionId, credentials: inputCredentials, ...(tipAmount && { tipAmount: BigInt(tipAmount) }), })); if (!result) return false; if (result instanceof GramJs.payments.PaymentVerificationNeeded) { sendApiUpdate({ '@type': 'updatePaymentVerificationNeeded', url: result.url, }); return undefined; } else { handleGramJsUpdate(result.updates); } return Boolean(result); } export async function sendStarPaymentForm({ formId, inputInvoice, }: { formId: string; inputInvoice: ApiRequestInputInvoice; }) { const result = await invokeRequest(new GramJs.payments.SendStarsForm({ formId: BigInt(formId), invoice: buildInputInvoice(inputInvoice), })); if (!result) return undefined; if (result instanceof GramJs.payments.PaymentVerificationNeeded) { if (DEBUG) { // eslint-disable-next-line no-console console.warn('Unexpected PaymentVerificationNeeded in sendStarsForm'); } return undefined; } handleGramJsUpdate(result.updates); if (inputInvoice.type === 'chatInviteSubscription') { const updates = 'updates' in result.updates ? result.updates.updates : undefined; const mtpChannelId = updates?.find((update): update is GramJs.UpdateChannel => ( update instanceof GramJs.UpdateChannel ))?.channelId; if (!mtpChannelId) { return undefined; } return { channelId: buildApiPeerId(mtpChannelId, 'channel'), }; } return { completed: true, }; } export async function getPaymentForm(inputInvoice: ApiRequestInputInvoice, theme?: ApiThemeParameters) { try { const result = await invokeRequest(new GramJs.payments.GetPaymentForm({ invoice: buildInputInvoice(inputInvoice), themeParams: theme ? buildInputThemeParams(theme) : undefined, }), { shouldThrow: true, }); if (!result) { return undefined; } return buildApiPaymentForm(result); } catch (err) { if (err instanceof Error) { // Can be removed if separate error handling is added to payment UI sendApiUpdate({ '@type': 'error', error: { message: err.message, hasErrorKey: true, }, }); return { error: err.message, }; } return undefined; } } export async function getReceipt(chat: ApiChat, msgId: number) { const result = await invokeRequest(new GramJs.payments.GetPaymentReceipt({ peer: buildInputPeer(chat.id, chat.accessHash), msgId, })); if (!result) { return undefined; } return { receipt: buildApiReceipt(result), }; } export async function fetchPremiumPromo() { const result = await invokeRequest(new GramJs.help.GetPremiumPromo()); if (!result) return undefined; result.videos.forEach((video) => { if (video instanceof GramJs.Document) { localDb.documents[video.id.toString()] = video; } }); return { promo: buildApiPremiumPromo(result), }; } export async function fetchTemporaryPaymentPassword(password: string) { const result = await getTemporaryPaymentPassword(password); if (!result) { return undefined; } if ('error' in result) { return result; } return { value: serializeBytes(result.tmpPassword), validUntil: result.validUntil, }; } export async function fetchMyBoosts() { const result = await invokeRequest(new GramJs.premium.GetMyBoosts()); if (!result) return undefined; const boosts = result.myBoosts.map(buildApiMyBoost); return { boosts, }; } export async function applyBoost({ chat, slots, } : { chat: ApiChat; slots: number[]; }) { const result = await invokeRequest(new GramJs.premium.ApplyBoost({ peer: buildInputPeer(chat.id, chat.accessHash), slots, })); if (!result) return undefined; const boosts = result.myBoosts.map(buildApiMyBoost); return { boosts, }; } export async function fetchBoostStatus({ chat, }: { chat: ApiChat; }) { const result = await invokeRequest(new GramJs.premium.GetBoostsStatus({ peer: buildInputPeer(chat.id, chat.accessHash), })); if (!result) { return undefined; } return buildApiBoostsStatus(result); } export async function fetchBoostList({ chat, isGifts, offset = '', limit, }: { chat: ApiChat; isGifts?: boolean; offset?: string; limit?: number; }) { const result = await invokeRequest(new GramJs.premium.GetBoostsList({ peer: buildInputPeer(chat.id, chat.accessHash), gifts: isGifts || undefined, offset, limit, })); if (!result) { return undefined; } const boostList = result.boosts.map(buildApiBoost); return { count: result.count, boostList, nextOffset: result.nextOffset, }; } export async function fetchGiveawayInfo({ peer, messageId, }: { peer: ApiPeer; messageId: number; }) { const result = await invokeRequest(new GramJs.payments.GetGiveawayInfo({ peer: buildInputPeer(peer.id, peer.accessHash), msgId: messageId, })); if (!result) { return undefined; } return buildApiGiveawayInfo(result); } export async function checkGiftCode({ slug, }: { slug: string; }) { const result = await invokeRequest(new GramJs.payments.CheckGiftCode({ slug, })); if (!result) { return undefined; } return { code: buildApiCheckedGiftCode(result), }; } export function applyGiftCode({ slug, }: { slug: string; }) { return invokeRequest(new GramJs.payments.ApplyGiftCode({ slug, }), { shouldReturnTrue: true, }); } export async function getPremiumGiftCodeOptions({ chat, }: { chat?: ApiChat; }) { const result = await invokeRequest(new GramJs.payments.GetPremiumGiftCodeOptions({ boostPeer: chat && buildInputPeer(chat.id, chat.accessHash), })); if (!result) { return undefined; } return result.map(buildApiPremiumGiftCodeOption); } export async function getStarsGiftOptions({ chat, }: { chat?: ApiChat; }) { const result = await invokeRequest(new GramJs.payments.GetStarsGiftOptions({ userId: chat && buildInputPeer(chat.id, chat.accessHash), })); if (!result) { return undefined; } return result.map(buildApiStarsGiftOptions); } export async function fetchStarsGiveawayOptions() { const result = await invokeRequest(new GramJs.payments.GetStarsGiveawayOptions()); if (!result) { return undefined; } return result.map(buildApiStarsGiveawayOptions); } export async function fetchStarGifts() { const result = await invokeRequest(new GramJs.payments.GetStarGifts({})); if (!result || result instanceof GramJs.payments.StarGiftsNotModified) { return undefined; } // Right now, only regular star gifts can be bought, but API are not specific return result.gifts.map(buildApiStarGift).filter((gift): gift is ApiStarGiftRegular => gift.type === 'starGift'); } export async function fetchSavedStarGifts({ peer, offset = '', limit, }: { peer: ApiPeer; offset?: string; limit?: number; }) { const result = await invokeRequest(new GramJs.payments.GetSavedStarGifts({ peer: buildInputPeer(peer.id, peer.accessHash), offset, limit, })); if (!result) { return undefined; } const gifts = result.gifts.map((g) => buildApiSavedStarGift(g, peer.id)); return { gifts, nextOffset: result.nextOffset, }; } export function saveStarGift({ inputGift, shouldUnsave, }: { inputGift: ApiRequestInputSavedStarGift; shouldUnsave?: boolean; }) { return invokeRequest(new GramJs.payments.SaveStarGift({ stargift: buildInputSavedStarGift(inputGift), unsave: shouldUnsave || undefined, })); } export function convertStarGift({ inputSavedGift, }: { inputSavedGift: ApiRequestInputSavedStarGift; }) { return invokeRequest(new GramJs.payments.ConvertStarGift({ stargift: buildInputSavedStarGift(inputSavedGift), })); } export function launchPrepaidGiveaway({ chat, giveawayId, paymentPurpose, }: { chat: ApiChat; giveawayId: string; paymentPurpose: ApiInputStorePaymentPurpose; }) { return invokeRequest(new GramJs.payments.LaunchPrepaidGiveaway({ peer: buildInputPeer(chat.id, chat.accessHash), giveawayId: BigInt(giveawayId), purpose: buildInputStorePaymentPurpose(paymentPurpose), }), { shouldReturnTrue: true, }); } export async function fetchStarsStatus() { const result = await invokeRequest(new GramJs.payments.GetStarsStatus({ peer: new GramJs.InputPeerSelf(), })); if (!result) { return undefined; } return { nextHistoryOffset: result.nextOffset, history: result.history?.map(buildApiStarsTransaction), nextSubscriptionOffset: result.subscriptionsNextOffset, subscriptions: result.subscriptions?.map(buildApiStarsSubscription), balance: buildApiStarsAmount(result.balance), }; } export async function fetchStarsTransactions({ peer, offset, isInbound, isOutbound, }: { peer?: ApiPeer; offset?: string; isInbound?: true; isOutbound?: true; }) { const inputPeer = peer ? buildInputPeer(peer.id, peer.accessHash) : new GramJs.InputPeerSelf(); const result = await invokeRequest(new GramJs.payments.GetStarsTransactions({ peer: inputPeer, offset, inbound: isInbound, outbound: isOutbound, })); if (!result) { return undefined; } return { nextOffset: result.nextOffset, history: result.history?.map(buildApiStarsTransaction), balance: buildApiStarsAmount(result.balance), }; } export async function fetchStarsTransactionById({ id, peer, }: { id: string; peer?: ApiPeer; }) { const inputPeer = peer ? buildInputPeer(peer.id, peer.accessHash) : new GramJs.InputPeerSelf(); const result = await invokeRequest(new GramJs.payments.GetStarsTransactionsByID({ peer: inputPeer, id: [new GramJs.InputStarsTransaction({ id, })], })); if (!result?.history?.[0]) { return undefined; } return { transaction: buildApiStarsTransaction(result?.history[0]), }; } export async function fetchStarsSubscriptions({ offset, peer, }: { offset?: string; peer?: ApiPeer; }) { const inputPeer = peer ? buildInputPeer(peer.id, peer.accessHash) : new GramJs.InputPeerSelf(); const result = await invokeRequest(new GramJs.payments.GetStarsSubscriptions({ peer: inputPeer, offset, })); if (!result?.subscriptions) { return undefined; } return { nextOffset: result.subscriptionsNextOffset, subscriptions: result.subscriptions.map(buildApiStarsSubscription), balance: buildApiStarsAmount(result.balance), }; } export async function changeStarsSubscription({ peer, subscriptionId, isCancelled, }: { peer?: ApiPeer; subscriptionId: string; isCancelled: boolean; }) { const result = await invokeRequest(new GramJs.payments.ChangeStarsSubscription({ peer: peer ? buildInputPeer(peer.id, peer.accessHash) : new GramJs.InputPeerSelf(), subscriptionId, canceled: isCancelled, })); return result; } export async function fulfillStarsSubscription({ peer, subscriptionId, }: { peer?: ApiPeer; subscriptionId: string; }) { const result = await invokeRequest(new GramJs.payments.FulfillStarsSubscription({ peer: peer ? buildInputPeer(peer.id, peer.accessHash) : new GramJs.InputPeerSelf(), subscriptionId, })); return result; } export async function fetchStarsTopupOptions() { const result = await invokeRequest(new GramJs.payments.GetStarsTopupOptions()); if (!result) { return undefined; } return result.map(buildApiStarTopupOption); } export async function fetchUniqueStarGift({ slug }: { slug: string; }) { const result = await invokeRequest(new GramJs.payments.GetUniqueStarGift({ slug })); if (!result) return undefined; const gift = buildApiStarGift(result.gift); if (gift.type !== 'starGiftUnique') return undefined; return gift; } export async function fetchStarGiftUpgradePreview({ giftId, }: { giftId: string; }) { const result = await invokeRequest(new GramJs.payments.GetStarGiftUpgradePreview({ giftId: BigInt(giftId), })); if (!result) { return undefined; } return result.sampleAttributes.map(buildApiStarGiftAttribute).filter(Boolean); } export function upgradeGift({ inputSavedGift, shouldKeepOriginalDetails, }: { inputSavedGift: ApiRequestInputSavedStarGift; shouldKeepOriginalDetails?: true; }) { return invokeRequest(new GramJs.payments.UpgradeStarGift({ stargift: buildInputSavedStarGift(inputSavedGift), keepOriginalDetails: shouldKeepOriginalDetails, }), { shouldReturnTrue: true, }); }