Merge commit '8f6767c010a78a1d5dad2b8172561095d828ab90'

This commit is contained in:
Isaac
2024-05-17 17:10:31 +04:00
16 changed files with 753 additions and 391 deletions

View File

@@ -119,8 +119,8 @@ public struct BotPaymentForm : Equatable {
public let passwordMissing: Bool
public let invoice: BotPaymentInvoice
public let paymentBotId: PeerId
public let providerId: PeerId
public let url: String
public let providerId: PeerId?
public let url: String?
public let nativeProvider: BotPaymentNativeProvider?
public let savedInfo: BotPaymentRequestedInfo?
public let savedCredentials: [BotPaymentSavedCredentials]
@@ -206,7 +206,7 @@ extension BotPaymentRequestedInfo {
}
}
private func _internal_parseInputInvoice(transaction: Transaction, source: BotPaymentInvoiceSource) -> Api.InputInvoice? {
func _internal_parseInputInvoice(transaction: Transaction, source: BotPaymentInvoiceSource) -> Api.InputInvoice? {
switch source {
case let .message(messageId):
guard let inputPeer = transaction.getPeer(messageId.peerId).flatMap(apiInputPeer) else {
@@ -321,6 +321,9 @@ func _internal_fetchBotPaymentInvoice(postbox: Postbox, network: Network, source
}
return TelegramMediaInvoice(title: title, description: description, photo: photo.flatMap(TelegramMediaWebFile.init), receiptMessageId: nil, currency: parsedInvoice.currency, totalAmount: 0, startParam: "", extendedMedia: nil, flags: parsedFlags, version: TelegramMediaInvoice.lastVersion)
case let .paymentFormStars(_, _, _, title, description, photo, invoice, _):
let parsedInvoice = BotPaymentInvoice(apiInvoice: invoice)
return TelegramMediaInvoice(title: title, description: description, photo: photo.flatMap(TelegramMediaWebFile.init), receiptMessageId: nil, currency: parsedInvoice.currency, totalAmount: 0, startParam: "", extendedMedia: nil, flags: [], version: TelegramMediaInvoice.lastVersion)
}
}
|> mapError { _ -> BotPaymentFormRequestError in }
@@ -354,32 +357,43 @@ func _internal_fetchBotPaymentForm(accountPeerId: PeerId, postbox: Postbox, netw
|> mapToSignal { result -> Signal<BotPaymentForm, BotPaymentFormRequestError> in
return postbox.transaction { transaction -> BotPaymentForm in
switch result {
case let .paymentForm(flags, id, botId, title, description, photo, invoice, providerId, url, nativeProvider, nativeParams, additionalMethods, savedInfo, savedCredentials, apiUsers):
let _ = title
let _ = description
let _ = photo
let parsedPeers = AccumulatedPeers(users: apiUsers)
updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers)
case let .paymentForm(flags, id, botId, title, description, photo, invoice, providerId, url, nativeProvider, nativeParams, additionalMethods, savedInfo, savedCredentials, apiUsers):
let _ = title
let _ = description
let _ = photo
let parsedPeers = AccumulatedPeers(users: apiUsers)
updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers)
let parsedInvoice = BotPaymentInvoice(apiInvoice: invoice)
var parsedNativeProvider: BotPaymentNativeProvider?
if let nativeProvider = nativeProvider, let nativeParams = nativeParams {
switch nativeParams {
case let .dataJSON(data):
parsedNativeProvider = BotPaymentNativeProvider(name: nativeProvider, params: data)
}
let parsedInvoice = BotPaymentInvoice(apiInvoice: invoice)
var parsedNativeProvider: BotPaymentNativeProvider?
if let nativeProvider = nativeProvider, let nativeParams = nativeParams {
switch nativeParams {
case let .dataJSON(data):
parsedNativeProvider = BotPaymentNativeProvider(name: nativeProvider, params: data)
}
let parsedSavedInfo = savedInfo.flatMap(BotPaymentRequestedInfo.init)
let parsedSavedCredentials = savedCredentials?.map({ savedCredentials -> BotPaymentSavedCredentials in
switch savedCredentials {
case let .paymentSavedCredentialsCard(id, title):
return .card(id: id, title: title)
}
}) ?? []
}
let parsedSavedInfo = savedInfo.flatMap(BotPaymentRequestedInfo.init)
let parsedSavedCredentials = savedCredentials?.map({ savedCredentials -> BotPaymentSavedCredentials in
switch savedCredentials {
case let .paymentSavedCredentialsCard(id, title):
return .card(id: id, title: title)
}
}) ?? []
let additionalPaymentMethods = additionalMethods?.map({ BotPaymentMethod(apiPaymentFormMethod: $0) }) ?? []
return BotPaymentForm(id: id, canSaveCredentials: (flags & (1 << 2)) != 0, passwordMissing: (flags & (1 << 3)) != 0, invoice: parsedInvoice, paymentBotId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(botId)), providerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(providerId)), url: url, nativeProvider: parsedNativeProvider, savedInfo: parsedSavedInfo, savedCredentials: parsedSavedCredentials, additionalPaymentMethods: additionalPaymentMethods)
let additionalPaymentMethods = additionalMethods?.map({ BotPaymentMethod(apiPaymentFormMethod: $0) }) ?? []
return BotPaymentForm(id: id, canSaveCredentials: (flags & (1 << 2)) != 0, passwordMissing: (flags & (1 << 3)) != 0, invoice: parsedInvoice, paymentBotId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(botId)), providerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(providerId)), url: url, nativeProvider: parsedNativeProvider, savedInfo: parsedSavedInfo, savedCredentials: parsedSavedCredentials, additionalPaymentMethods: additionalPaymentMethods)
case let .paymentFormStars(flags, id, botId, title, description, photo, invoice, apiUsers):
let _ = flags
let _ = title
let _ = description
let _ = photo
let parsedPeers = AccumulatedPeers(users: apiUsers)
updatePeers(transaction: transaction, accountPeerId: accountPeerId, peers: parsedPeers)
let parsedInvoice = BotPaymentInvoice(apiInvoice: invoice)
return BotPaymentForm(id: id, canSaveCredentials: false, passwordMissing: false, invoice: parsedInvoice, paymentBotId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(botId)), providerId: nil, url: nil, nativeProvider: nil, savedInfo: nil, savedCredentials: [], additionalPaymentMethods: [])
}
}
|> mapError { _ -> BotPaymentFormRequestError in }

View File

@@ -104,11 +104,8 @@ private func requestStarsState(account: Account, peerId: EnginePeer.Id, offset:
var parsedTransactions: [StarsContext.State.Transaction] = []
for entry in history {
switch entry {
case let .starsTransaction(id, stars, date, peer):
if let peer = transaction.getPeer(peer.peerId) {
parsedTransactions.append(StarsContext.State.Transaction(id: id, count: stars, date: date, peer: EnginePeer(peer)))
}
if let parsedTransaction = StarsContext.State.Transaction(apiTransaction: entry, transaction: transaction) {
parsedTransactions.append(parsedTransaction)
}
}
return InternalStarsStatus(balance: balance, transactions: parsedTransactions, nextOffset: nextOffset)
@@ -136,6 +133,7 @@ private final class StarsContextImpl {
private var nextOffset: String?
private let disposable = MetaDisposable()
private var updateDisposable: Disposable?
init(account: Account, peerId: EnginePeer.Id) {
assert(Queue.mainQueue().isCurrent())
@@ -147,11 +145,20 @@ private final class StarsContextImpl {
self._statePromise.set(.single(nil))
self.load()
self.updateDisposable = (account.stateManager.updatedStarsBalance()
|> deliverOnMainQueue).startStrict(next: { [weak self] balances in
guard let self, let state = self._state, let balance = balances[peerId] else {
return
}
self._state = StarsContext.State(balance: balance, transactions: state.transactions)
})
}
deinit {
assert(Queue.mainQueue().isCurrent())
self.disposable.dispose()
self.updateDisposable?.dispose()
}
func load() {
@@ -190,15 +197,45 @@ private final class StarsContextImpl {
}
}
private extension StarsContext.State.Transaction {
init?(apiTransaction: Api.StarsTransaction, transaction: Transaction) {
switch apiTransaction {
case let .starsTransaction(id, stars, date, transactionPeer):
let parsedPeer: StarsContext.State.Transaction.Peer
switch transactionPeer {
case .starsTransactionPeerAppStore:
parsedPeer = .appStore
case .starsTransactionPeerPlayMarket:
parsedPeer = .playMarket
case .starsTransactionPeerFragment:
parsedPeer = .fragment
case let .starsTransactionPeer(apiPeer):
guard let peer = transaction.getPeer(apiPeer.peerId) else {
return nil
}
parsedPeer = .peer(EnginePeer(peer))
}
self.init(id: id, count: stars, date: date, peer: parsedPeer)
}
}
}
public final class StarsContext {
public struct State: Equatable {
public struct Transaction: Equatable {
public enum Peer: Equatable {
case appStore
case playMarket
case fragment
case peer(EnginePeer)
}
public let id: String
public let count: Int64
public let date: Int32
public let peer: EnginePeer
public let peer: Peer
init(id: String, count: Int64, date: Int32, peer: EnginePeer) {
init(id: String, count: Int64, date: Int32, peer: Peer) {
self.id = id
self.count = count
self.date = date
@@ -245,3 +282,79 @@ public final class StarsContext {
})
}
}
func _internal_sendStarsPaymentForm(account: Account, formId: Int64, source: BotPaymentInvoiceSource) -> Signal<SendBotPaymentResult, SendBotPaymentFormError> {
return account.postbox.transaction { transaction -> Api.InputInvoice? in
return _internal_parseInputInvoice(transaction: transaction, source: source)
}
|> castError(SendBotPaymentFormError.self)
|> mapToSignal { invoice -> Signal<SendBotPaymentResult, SendBotPaymentFormError> in
guard let invoice = invoice else {
return .fail(.generic)
}
let flags: Int32 = 0
return account.network.request(Api.functions.payments.sendStarsForm(flags: flags, formId: formId, invoice: invoice))
|> map { result -> SendBotPaymentResult in
switch result {
case let .paymentResult(updates):
account.stateManager.addUpdates(updates)
var receiptMessageId: MessageId?
for apiMessage in updates.messages {
if let message = StoreMessage(apiMessage: apiMessage, accountPeerId: account.peerId, peerIsForum: false) {
for media in message.media {
if let action = media as? TelegramMediaAction {
if case .paymentSent = action.action {
switch source {
case let .slug(slug):
for media in message.media {
if let action = media as? TelegramMediaAction, case let .paymentSent(_, _, invoiceSlug?, _, _) = action.action, invoiceSlug == slug {
if case let .Id(id) = message.id {
receiptMessageId = id
}
}
}
case let .message(messageId):
for attribute in message.attributes {
if let reply = attribute as? ReplyMessageAttribute {
if reply.messageId == messageId {
if case let .Id(id) = message.id {
receiptMessageId = id
}
}
}
}
case let .premiumGiveaway(_, _, _, _, _, _, randomId, _, _, _, _):
if message.globallyUniqueId == randomId {
if case let .Id(id) = message.id {
receiptMessageId = id
}
}
case .giftCode:
receiptMessageId = nil
case .stars:
receiptMessageId = nil
}
}
}
}
}
}
return .done(receiptMessageId: receiptMessageId)
case let .paymentVerificationNeeded(url):
return .externalVerificationRequired(url: url)
}
}
|> `catch` { error -> Signal<SendBotPaymentResult, SendBotPaymentFormError> in
if error.errorDescription == "BOT_PRECHECKOUT_FAILED" {
return .fail(.precheckoutFailed)
} else if error.errorDescription == "PAYMENT_FAILED" {
return .fail(.paymentFailed)
} else if error.errorDescription == "INVOICE_ALREADY_PAID" {
return .fail(.alreadyPaid)
}
return .fail(.generic)
}
}
}

View File

@@ -29,7 +29,7 @@ public extension TelegramEngine {
public func sendBotPaymentForm(source: BotPaymentInvoiceSource, formId: Int64, validatedInfoId: String?, shippingOptionId: String?, tipAmount: Int64?, credentials: BotPaymentCredentials) -> Signal<SendBotPaymentResult, SendBotPaymentFormError> {
return _internal_sendBotPaymentForm(account: self.account, formId: formId, source: source, validatedInfoId: validatedInfoId, shippingOptionId: shippingOptionId, tipAmount: tipAmount, credentials: credentials)
}
public func requestBotPaymentReceipt(messageId: MessageId) -> Signal<BotPaymentReceipt, RequestBotPaymentReceiptError> {
return _internal_requestBotPaymentReceipt(account: self.account, messageId: messageId)
}
@@ -73,5 +73,9 @@ public extension TelegramEngine {
public func peerStarsContext(peerId: EnginePeer.Id) -> StarsContext {
return StarsContext(account: self.account, peerId: peerId)
}
}
public func sendStarsPaymentForm(formId: Int64, source: BotPaymentInvoiceSource) -> Signal<SendBotPaymentResult, SendBotPaymentFormError> {
return _internal_sendStarsPaymentForm(account: self.account, formId: formId, source: source)
}
}
}