IAP
IAP的概念
# IAP流程3种实体: 客户 , 服务提供者 和 Odoo自身
# 客户 安装提供服务的模块,即可使用这个模块
# 服务 提供给开发人员。 客户的有足够的余额,服务提供者会扣除余额并提供服务
# Odoo 作为中间商 25%的佣金
# IAP服务流
1. 客户 向 服务提供商发起 提供服务请求。 客户将传递账号令牌,服务提供商使用令牌来识别用户
2. 接受到客户请求服务后。 服务商检查 Odoo IAP客户的账号里是否有足够的余额。没有余额则拒绝掉本次服务
3. 在保留余额后,服务者执行服务。 某些情况下,服务提供者会调用外部服务来执行所请求的服务
4. 执行完服务后,服务提供者回到Odoo API来获取所预留的余额。 如果请求的服务未能正常提供服务,服务提供者会要求Odoo释放所预留余额
5. 服务提供者会回到客户。 通知他们请求服务成功,返回一些成功信息
# 余额不足时
1.服务提供者返回到客户并告知他们在账户中没有足够的余额,显示用户可以购买服务的信息(一个Odoo服务包链接)。
2. 客户重定向到Odoo并进行服务的充值。
注册OdooIAP服务
# https://iap.odoo.com/
# 注册一个IAP服务
# 1. 保存 秘钥
创建IAP模块
# 开发一个 提供 IAP 服务的模块
# 1. 模型, 视图,
pass 跳过
# 2. 定义要处理 当前模型的函数
@api.model
def _books_data_by_isbn(self, isbn):
book = self.search([('isbn', '=', isbn)], limit=1) # 搜索到isbn 这本书 ,并返回
if book:
return {
'status': 'found',
'data': {
'name': book.name,
'isbn': book.isbn,
'date_release': book.date_release,
'cover_image': book.cover_image,
'authors': [a.name for a in book.author_ids]
}
}
else:
return {
'status': 'not found',
}
# 3. 在controllers 中 配置一个 处理接口
# ir.config_parameter 系统参数模型
# account_token 客户令牌
@http.route('/get_book_data', type='json', auth="public") # type=限定传输数据类型为json
def get_book_data(self, account_token, isbn_number):
# 获取 service_key ,
service_key = request.env['ir.config_parameter'].sudo().get_param('iap.isbn_service_key', False)
if not service_key:
return {
'status': 'service is not active'
}
credits_to_reserve = 1
data = {}
# iap.charge 方法 。 处理从客户账户中获取余额的流程,参数:环境,服务秘钥,客户账户令牌和获取的金额
# 此方法会创建上下文, 异常时 在 __emter__ 抛出异常
# credit_template 是 指定模型调用的模板
with iap.charge(request.env, service_key, account_token, credits_to_reserve,
credit_template='iap_isbn_service.no_credit_info'):
data = request.env['book.info'].sudo()._books_data_by_isbn(isbn_number)
if data['status'] == 'not found':
raise Exception('Book not found')
return data
授权并收取IAP余额
# 1. 设置 isbn_server_key 字段。 在配置中添加
from odoo import models, fields
class ConfigSettings(models.TransientModel):
_inherit = 'res.config.settings'
# config_parameter 参数 是指定存储到ir.config_paramete 模型中 ,称之为系统参数
# 获取系统参数可以通过 get_param() 函数
# `self.env['ir.config_parameter'].sudo().get_param('iap.isbn_service_key',False)`
isbn_service_key = fields.Char("ISBN service key", config_parameter='iap.isbn_service_key')
# 2. account_token 是标识用户的客户令牌
account_token 是 服务端价差当前客户余额时,服务者发送给客户端的令牌
# 3. 调用 iap.charge() 四个参数:环境,服务秘钥,客户令牌,获取的金额
# 没有足够余额时 , 会抛出InsufficientCreditError错误
# 多条数据 请求 iap服务时的方法
def xxx(self,account_token,isbn_list=None):
.....
isbn_list = [el for el in isbn_list] # 多条数据 , 调用服务
credits_to_reserve = len(isbn_list)
data_fond = []
with iap.charge(request.env, service_key, account_token, credits_to_reserve,
credit_template='iap_isbn_service.no_credit_info') as transection:
for isbn in isbn_list:
data = request.env['book.info'].sudo()._books_data_by_isbn(isbn)
if data['status'] == 'not found':
data_fond.append(data_fond)
transection.credit = len(data_fond)
return data_fond
# 4. 确保 它能够处理 JSON-RPC2的请求
创建IAP客户端模块
# 1. 添加 button 视图,对应到具体方法
<button name="fetch_book_data"
string="Fetch Book Data" type="object"/>
# 2. 发起调用服务的请求
def fetch_book_data(self):
self.ensure_one() # 确保是一条数据
if not self.isbn: # 如果 这条数据没由isbn 书号,抛出用户错误
raise UserError("Please add ISBN number")
user_token = self.env['iap.account'].get('book_isbn') # 用户调用注册过的服务,服务端生成 令牌
params = {
'account_token': user_token.account_token,
'isbn_number': self.isbn
}
service_endpoint = 'http://localhost:8888'
result = jsonrpc(service_endpoint + '/get_book_data', params=params)
if result.get('status') == 'found':
self.write(self.process_result(result['data']))
return True
# process_result() 函数调用 服务完成后处理的结果
@api.model
def process_result(self, result):
authors = []
existing_author_ids = []
for author_name in result['authors']:
author = self.env['res.partner'].search([('name', '=', author_name)], limit=1)
if author:
existing_author_ids.append(author.id)
else:
authors.append((0, 0, {'name': author_name}))
if existing_author_ids:
authors.append((6, 0, existing_author_ids))
return {
'author_ids': authors,
'name': result.get('name'),
'isbn': result.get('isbn'),
'cover_image': result.get('cover_image'),
'date_release': result.get('date_release'),
}