-
Notifications
You must be signed in to change notification settings - Fork 2k
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
Google billing interface uses promises #17996
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -28,7 +28,7 @@ interface BillingEventMap { | |
[google.BillingEventType.BILLING_SETUP_FINISHED]: (result: google.BillingResult) => void, | ||
[google.BillingEventType.BILLING_SERVICE_DISCONNECTED]: () => void, | ||
[google.BillingEventType.PRODUCT_DETAILS_RESPONSE]: | ||
(result: google.BillingResult, productDetailsList: google.ProductDetails[]) => void, | ||
(result: google.BillingResult, productDetailsList: google.ProductDetails[]) => void, | ||
[google.BillingEventType.PURCHASES_UPDATED]: (result: google.BillingResult, purchases: google.Purchase[]) => void, | ||
[google.BillingEventType.CONSUME_RESPONSE]: (result: google.BillingResult, purchaseToken: string) => void, | ||
[google.BillingEventType.ACKNOWLEDGE_PURCHASES_RESPONSE]: (result: google.BillingResult) => void | ||
|
@@ -131,8 +131,16 @@ class Billing { | |
* @en Starts up BillingClient setup process asynchronously. | ||
* @zh 异步启动 BillingClient 设置过程。 | ||
*/ | ||
public startConnection (): void { | ||
jsb.googleBilling?.startConnection(); | ||
public startConnection (): Promise<google.BillingResult> { | ||
return new Promise((resolve, reject) => { | ||
this.once(google.BillingEventType.BILLING_SETUP_FINISHED, (result: google.BillingResult): void => { | ||
resolve(result); | ||
}); | ||
this.once(google.BillingEventType.BILLING_SERVICE_DISCONNECTED, (): void => { | ||
reject(); | ||
}); | ||
jsb.googleBilling?.startConnection(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need to reject if |
||
}); | ||
} | ||
|
||
/** | ||
|
@@ -176,8 +184,20 @@ class Billing { | |
* @param productType @zh 产品类型。 @en product type. | ||
* | ||
*/ | ||
public queryProductDetailsParams (productId: string[], productType: google.ProductType): void { | ||
jsb.googleBilling?.queryProductDetailsParams(productId, productType); | ||
public queryProductDetailsParams (productId: string[], productType: google.ProductType): Promise<google.ProductDetails[]> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 这个 Promise 返回的对象是错误的,没有和 Google Billing 的API保持一致 当前的返回列表信息 预期正确的返回 https://developer.android.com/google/play/billing/integrate?hl=zh-cn#show-products There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 我们现在是希望封装得对 ts 开发者友好点。 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 同理,其他很多公开API也是一样的问题,请尽量保持和GoogleBillingAPI一致的返回结果,很多时候,BillingResult 是一个很重要的返回参数,实际项目开发中也会用到此参数做业务判断,不能忽略返回此参数 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
这个支付 sdk 很重要,项目的命脉,个人建议最好是和官方的 API 保持一致,而不是过度封装 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 接口都一样,只是说回调的方式对 ts 更友好点。担心 ts 的开发者不习惯 java 风格的 api。 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 感觉你们过于忽视对 BilingResult 相应状态码的处理,Google 甚至专门有一个文档链接详细介绍处理的 https://developer.android.com/google/play/billing/errors?hl=zh-cn 特别是这句: 所以,Google Billing 的API ,很多地方才返回这个 BillingResult 状态对象,因为是真的很多状态,并不是非对即错,一般业务都需要根据不同状态做对应的处理 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 此 PR 前的 ts API 本身也跟 Java 的 API 风格不一致。 // ts
public queryProductDetailsParams (productId: string[], productType: google.ProductType): void {
...
} // java
billingClient.queryProductDetailsAsync(
queryProductDetailsParams,
new ProductDetailsResponseListener() {
public void onProductDetailsResponse(BillingResult billingResult,
List<ProductDetails> productDetailsList) {
// check billingResult
// process returned productDetailsList
}
}
) 如果是跟 java 风格一致,那么这个接口的 ts 声明应该是: class QueryProductDetailsParams {
public static newBuilder(): QueryProductDetailsParamsBuilder;
}
class QueryProductDetailsParamsBuilder {
public setProductList( ... );
public build(): QueryProductDetailsParams;
}
interface ProductDetailsResponseListener {
onProductDetailsResponse (billingResult: BillingResult, productDetailsList: ProductDetails[]) ) => void;
}
queryProductDetailsAsync(params: QueryProductDetailsParams, listener: ProductDetailsResponseListener): void;
// 用法:
queryProductDetailsAsync(
QueryProductDetailsParams.newBuilder().setProductList(...).build(), {
onProductDetailsResponse (billingResult: BillingResult, productDetailsList: ProductDetails[]) {
// check billingResult
// process returned productDetailsList
}
}
); There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 另外,为什么 ts 封装的接口 productType: google.ProductType 这个参数只支持传递一个?java 的接口是 id 与 type 的结构体的数组。这是不是也导致了不一致的行为? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 大佬们有点跑题了 请注意:我这里原本的问题是,保持一致的 感觉大佬们现在误解我的意思了 现在这里的代码的问题是过度封装,返回参接只是返回了商品列表 在返回参数没保持一致的前提下,我再看了这个代码的 原来的 API 是可以这样子的 一次性请求不同类型的商品(消耗型、订阅型)不同id 但是现在的 API 不能做到 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 这个后面会调整 |
||
return new Promise((resolve, reject) => { | ||
this.once( | ||
google.BillingEventType.PRODUCT_DETAILS_RESPONSE, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 这里的 id 是唯一的,存在非常严重的问题。 当我连续
正确的做法应该是在调用此接口的时候,实时生成一个回调事件(uuid之类),同时ts注册这个事件的回调监听,然后在 Android 端获取到结果的时候,回调对应的事件 注意:其他API需要检查,很大概率存在类似问题 |
||
(result: google.BillingResult, productDetailsList: google.ProductDetails[]): void => { | ||
if (result.responseCode === google.BillingResponseCode.OK) { | ||
resolve(productDetailsList); | ||
} else { | ||
reject(result); | ||
} | ||
}, | ||
); | ||
jsb.googleBilling?.queryProductDetailsParams(productId, productType); | ||
}); | ||
} | ||
|
||
/** | ||
|
@@ -186,76 +206,184 @@ class Billing { | |
* @param productDetails @zh 产品详情。 @en product details. | ||
* @param selectedOfferToken @zh 选择提供的token。 @en selected offer token. | ||
*/ | ||
public launchBillingFlow (productDetails: google.ProductDetails[], selectedOfferToken: string | null): void { | ||
jsb.googleBilling?.launchBillingFlow(productDetails, selectedOfferToken); | ||
public launchBillingFlow (productDetails: google.ProductDetails[], selectedOfferToken: string | null): Promise<google.Purchase[]> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 这里不应该是一个异步方法,更不应该是异步返回一个交易列表,正确的应该是一个同步方法,并且结果是返回此操作的状态码,详见 https://developer.android.com/google/play/billing/integrate?hl=zh-cn#launch There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. PurchaseUpdate 的回调,不一定是主动触发 launchBilingFlow 后回调的 它是 Google 那边在连接成功后,任何时间 都可能会触发的一个回调。 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 现在过度封装后的 https://developer.android.com/google/play/billing/integrate?hl=zh-cn#launch launchBillingFlow 是拉起支付界面,但并不是一定能拉起成功,拉起成功也并不是一定会创建交易(比如用户拉起之后,就取消支付) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 这个地方没有必要保持一致,连接成功之后,基本上不会出错,内部也有连接是否成功的判断。因为cocos和UI不是在同一个线程,很难做到保持一致。 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
这个倒是没有注意到的问题。我看看 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
要的! https://developer.android.com/google/play/billing/errors?hl=zh-cn#item_already_owned There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
return new Promise((resolve, reject) => { | ||
this.once( | ||
google.BillingEventType.PURCHASES_UPDATED, | ||
(result: google.BillingResult, purchaseList: google.Purchase[]): void => { | ||
if (result.responseCode === google.BillingResponseCode.OK) { | ||
resolve(purchaseList); | ||
} else { | ||
reject(result); | ||
} | ||
}, | ||
); | ||
jsb.googleBilling?.launchBillingFlow(productDetails, selectedOfferToken); | ||
}); | ||
} | ||
|
||
/** | ||
* @en Consumes a given in-app product. | ||
* @zh 消费指定的应用内产品。 | ||
* @param purchase @zh 已经购买的产品。 @en Purchased Products. | ||
*/ | ||
public consumePurchases (purchase: google.Purchase[]): void { | ||
jsb.googleBilling?.consumePurchases(purchase); | ||
public consumePurchases (purchase: google.Purchase[]): Promise<string> { | ||
return new Promise((resolve, reject) => { | ||
this.once( | ||
google.BillingEventType.CONSUME_RESPONSE, | ||
(result: google.BillingResult, token: string): void => { | ||
if (result.responseCode === google.BillingResponseCode.OK) { | ||
resolve(token); | ||
} else { | ||
reject(result); | ||
} | ||
}, | ||
); | ||
jsb.googleBilling?.consumePurchases(purchase); | ||
}); | ||
} | ||
|
||
/** | ||
* @en Acknowledges in-app purchases. | ||
* @zh 确认应用内购买。 | ||
* @param purchase @zh 已经购买的产品。 @en Purchased Products. | ||
*/ | ||
public acknowledgePurchase (purchase: google.Purchase[]): void { | ||
jsb.googleBilling?.acknowledgePurchase(purchase); | ||
public acknowledgePurchase (purchase: google.Purchase[]): Promise<void> { | ||
return new Promise((resolve, reject) => { | ||
this.once( | ||
google.BillingEventType.ACKNOWLEDGE_PURCHASES_RESPONSE, | ||
(result: google.BillingResult): void => { | ||
if (result.responseCode === google.BillingResponseCode.OK) { | ||
resolve(); | ||
} else { | ||
reject(result); | ||
} | ||
}, | ||
); | ||
jsb.googleBilling?.acknowledgePurchase(purchase); | ||
}); | ||
} | ||
|
||
/** | ||
* @en Returns purchases details for currently owned items bought within your app. | ||
* @zh 返回您应用内当前拥有的购买商品的购买详情。 | ||
* @param productType @zh 产品类型 @en Product type. | ||
*/ | ||
public queryPurchasesAsync (productType: google.ProductType): void { | ||
jsb.googleBilling?.queryPurchasesAsync(productType); | ||
public queryPurchasesAsync (productType: google.ProductType): Promise<google.Purchase[]> { | ||
return new Promise((resolve, reject) => { | ||
this.once( | ||
google.BillingEventType.QUERY_PURCHASES_RESPONSE, | ||
(result: google.BillingResult, purchaseList: google.Purchase[]): void => { | ||
if (result.responseCode === google.BillingResponseCode.OK) { | ||
resolve(purchaseList); | ||
} else { | ||
reject(result); | ||
} | ||
}, | ||
); | ||
jsb.googleBilling?.queryPurchasesAsync(productType); | ||
}); | ||
} | ||
|
||
/** | ||
* @en Gets the billing config, which stores configuration used to perform billing operations. | ||
* @zh 获取计费配置,其中存储用于执行计费操作的配置。 | ||
*/ | ||
public getBillingConfigAsync (): void { | ||
jsb.googleBilling?.getBillingConfigAsync(); | ||
public getBillingConfigAsync (): Promise<google.BillingConfig> { | ||
return new Promise((resolve, reject) => { | ||
this.once( | ||
google.BillingEventType.BILLING_CONFIG_RESPONSE, | ||
(result: google.BillingResult, billingConfig: google.BillingConfig): void => { | ||
if (result.responseCode === google.BillingResponseCode.OK) { | ||
resolve(billingConfig); | ||
} else { | ||
reject(result); | ||
} | ||
}, | ||
); | ||
jsb.googleBilling?.getBillingConfigAsync(); | ||
}); | ||
} | ||
|
||
/** | ||
* @en Creates alternative billing only purchase details that can be used to report a transaction made | ||
* via alternative billing without user choice to use Google Play billing. | ||
* @zh 创建仅限替代结算的购买详情,可用于报告通过替代结算进行的交易,而无需用户选择使用 Google Play 结算。 | ||
*/ | ||
public createAlternativeBillingOnlyReportingDetailsAsync (): void { | ||
jsb.googleBilling?.createAlternativeBillingOnlyReportingDetailsAsync(); | ||
public createAlternativeBillingOnlyReportingDetailsAsync (): Promise<void> { | ||
return new Promise((resolve, reject) => { | ||
this.once( | ||
google.BillingEventType.ALTERNATIVE_BILLING_ONLY_TOKEN_RESPONSE, | ||
(result: google.BillingResult): void => { | ||
if (result.responseCode === google.BillingResponseCode.OK) { | ||
resolve(); | ||
} else { | ||
reject(result); | ||
} | ||
}, | ||
); | ||
jsb.googleBilling?.createAlternativeBillingOnlyReportingDetailsAsync(); | ||
}); | ||
} | ||
|
||
/** | ||
* @en Checks the availability of offering alternative billing without user choice to use Google Play billing. | ||
* @zh 检查是否可以提供替代结算方式,而无需用户选择使用 Google Play 结算方式。 | ||
*/ | ||
public isAlternativeBillingOnlyAvailableAsync (): void { | ||
jsb.googleBilling?.isAlternativeBillingOnlyAvailableAsync(); | ||
public isAlternativeBillingOnlyAvailableAsync (): Promise<void> { | ||
return new Promise((resolve, reject) => { | ||
this.once( | ||
google.BillingEventType.EXTERNAL_OFFER_REPORTING_DETAILS_RESPONSE, | ||
(result: google.BillingResult): void => { | ||
if (result.responseCode === google.BillingResponseCode.OK) { | ||
resolve(); | ||
} else { | ||
reject(result); | ||
} | ||
}, | ||
); | ||
jsb.googleBilling?.isAlternativeBillingOnlyAvailableAsync(); | ||
}); | ||
} | ||
|
||
/** | ||
* @en Creates purchase details that can be used to report a transaction made via external offer. | ||
* @zh 创建可用于报告通过外部报价进行的交易的购买详情。 | ||
*/ | ||
public createExternalOfferReportingDetailsAsync (): void { | ||
jsb.googleBilling?.createExternalOfferReportingDetailsAsync(); | ||
public createExternalOfferReportingDetailsAsync (): Promise<void> { | ||
return new Promise((resolve, reject) => { | ||
this.once( | ||
google.BillingEventType.EXTERNAL_OFFER_REPORTING_DETAILS_RESPONSE, | ||
(result: google.BillingResult): void => { | ||
if (result.responseCode === google.BillingResponseCode.OK) { | ||
resolve(); | ||
} else { | ||
reject(result); | ||
} | ||
}, | ||
); | ||
jsb.googleBilling?.createExternalOfferReportingDetailsAsync(); | ||
}); | ||
} | ||
|
||
/** | ||
* @en Checks the availability of providing external offer. | ||
* @zh 检查提供外部报价的可用性。 | ||
*/ | ||
public isExternalOfferAvailableAsync (): void { | ||
jsb.googleBilling?.isExternalOfferAvailableAsync(); | ||
public isExternalOfferAvailableAsync (): Promise<void> { | ||
return new Promise((resolve, reject) => { | ||
this.once( | ||
google.BillingEventType.EXTERNAL_OFFER_AVAILABILITY_RESPONSE, | ||
(result: google.BillingResult): void => { | ||
if (result.responseCode === google.BillingResponseCode.OK) { | ||
resolve(); | ||
} else { | ||
reject(result); | ||
} | ||
}, | ||
); | ||
jsb.googleBilling?.isExternalOfferAvailableAsync(); | ||
}); | ||
} | ||
|
||
/** | ||
|
@@ -307,15 +435,15 @@ class Billing { | |
return null; | ||
} | ||
|
||
public on<K extends keyof BillingEventMap> (type: K, callback: BillingEventMap[K], target?: unknown): BillingEventMap[K] { | ||
private on<K extends keyof BillingEventMap> (type: K, callback: BillingEventMap[K], target?: unknown): BillingEventMap[K] { | ||
this._eventTarget.on(type, callback, target); | ||
return callback; | ||
} | ||
public once<K extends keyof BillingEventMap> (type: K, callback: BillingEventMap[K], target?: unknown): BillingEventMap[K] { | ||
private once<K extends keyof BillingEventMap> (type: K, callback: BillingEventMap[K], target?: unknown): BillingEventMap[K] { | ||
this._eventTarget.once(type, callback, target); | ||
return callback; | ||
} | ||
public off<K extends keyof BillingEventMap> (eventType: K, callback?: BillingEventMap[K], target?: any): void { | ||
private off<K extends keyof BillingEventMap> (eventType: K, callback?: BillingEventMap[K], target?: any): void { | ||
this._eventTarget.off(eventType, callback, target); | ||
} | ||
} | ||
|
@@ -816,7 +944,6 @@ export namespace google { | |
export type AlternativeBillingOnlyReportingDetails = jsb.AlternativeBillingOnlyReportingDetails; | ||
export type ExternalOfferReportingDetails = jsb.ExternalOfferReportingDetails; | ||
export type InAppMessageResult = jsb.InAppMessageResult; | ||
|
||
/** | ||
* @en | ||
* Interface for Google Play blling module. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we don’t use an id for callbacks, could it lead to timing issues and result in incorrect outcomes?