3


记帐 API

Sun Java System Content Delivery Server 记帐 API 提供 Content Delivery Server 与记帐系统之间的接口。使用记帐 API 可以定制创建支持预付记帐或异步记帐的记帐适配器。

记帐 API 包含以下类和接口:

记帐 API 的类和接口位于 com.sun.content.server.billing 软件包中。有关这些类和接口的其他信息,请参见 $CDS_HOME/javadoc/cdsapi/index.html 中的 Javadoctrademark 工具的 HTML 输出。


3.1 一般处理流程

记帐 API 用于处理 Content Delivery Server 与外部记帐系统之间的通信。记帐事务的详细信息保留在 BillingInfo 对象中。记帐适配器是 BillingManager 接口的实现,它用于确定事务的处理方式。

本节介绍 Content Delivery Server 与外部记帐系统之间的以下信息流:

有关本节涉及的 BillingManager 方法的详细信息,请参见 3.2 BillingManager 接口

3.1.1 内容列表

订户请求可用内容列表或 Vending Manager 管理员请求储存内容列表时将启动该列表进程。图 3-1 显示了此进程。

图 3-1 内容列表的进程流


有关获取内容列表的概述。此图以文本形式进行说明。

以下各项提供了有关列出内容进程的其他详细信息:

1. 当订户或 Vending Manager 管理员请求内容列表时,Content Delivery Server 将为列表中每个对象创建初始 BillingInfo 对象,并调用记帐适配器的 getBillingInfos 方法。

2. 如果需要,getBillingInfos 的实现可以修改购买每个事务的价格或其他详细信息。您还可以指定在购买时是否需要授权,以及在成功下载内容后是否需要确认消息。这些详细信息在返回到 Content Delivery Server 的 BillingInfo 对象中进行设置。有关其他信息,请参见 3.2.6 getBillingInfos 方法

3. Content Delivery Server 使用每个返回的 BillingInfo 对象展示所显示的列表中的定价信息。

3.1.2 事务启动

当订户单击内容项旁边的“查看详细信息”时,将会启动记帐事务。图 3-2 显示了此进程。

图 3-2 事务启动的进程流


有关事务启动的概述。此图以文本形式进行说明。

以下各项提供了有关事务启动进程的其他详细信息:

1. 当订户请求项目的详细信息时,Content Delivery Server 将根据 Content Delivery Server 数据库中的信息为订户的选择创建初始 BillingInfo 对象。

2. 然后 Content Delivery Server 将调用记帐适配器的 getBillingInfo 方法,并将 BillingInfo 对象传递给该方法。

3. 如果需要,getBillingInfo 方法的实现可以修改购买价格或事务的其他详细信息。您还可以指定在购买时是否需要授权,以及在成功下载内容后是否需要确认消息。这些详细信息在返回到 Content Delivery Server 的 BillingInfo 对象中进行设置。有关其他信息,请参见 3.2.5 getBillingInfo 方法

4. Content Delivery Server 使用返回的 BillingInfo 对象向订户展示选定项目的内容详细资料和定价信息。

3.1.3 订户购买

当订户选择某个内容项并单击“购买”时,将会启动购买进程。购买进程将使用启动事务时创建的 BillingInfo 对象。图 3-3 显示了此进程。

图 3-3 订户购买的进程流


有关订户购买的概述。此图以文本形式进行说明。

以下各项提供了有关订户购买进程的其他详细信息:

1. 如果订户选择购买内容,Content Delivery Server 将检查由 getBillingInfo 方法返回的 BillingInfo 对象,以查看是否需要授权。

2. 如果需要授权,Content Delivery Server 将调用记帐适配器的 authorize 方法,并传递 BillingInfo 对象。此方法用于确定是否允许订户购买内容,并在 BillingInfo 对象中设置 "OK" 标志。然后该对象将返回到 Content Delivery Server。有关其他信息,请参见 3.2.1 authorize 方法

3. 如果订户被授权购买内容或无需授权,则显示购买页面。如果订户未被授权购买内容,则发出错误消息。

订户购买内容时,与事务关联的 BillingInfo 对象存储在 Content Delivery Server 数据库中。如有任何进一步操作需要记帐信息,Content Delivery Server 就会以订户 ID 和内容 ID 为关键字从数据库中检索对象。

3.1.4 下载确认

当设备完成下载并通知 Content Delivery Server 时,将会启动确认进程。图 3-4 显示了此进程。如果下载成功并且记帐系统不需要通知,则不执行任何操作。

图 3-4 下载确认的进程流


有关处理下载确认的概述。此图以文本形式进行说明。

以下各项提供了有关下载确认进程的其他详细信息:

1. 当 Content Delivery Server 从设备收到下载成功的确认消息时,Content Delivery Server 将检查记帐信息,以查看是否需要将该确认消息通知记帐系统。

2. 如果需要确认,Content Delivery Server 将调用记帐适配器的 confirm 方法。有关其他信息,请参见 3.2.3 confirm 方法

3. 如果设备返回的不是成功确认的消息,而是错误消息,Content Delivery Server 将调用记帐适配器的 reverse 方法,并传递此事务的 BillingInfo 对象。对 reverse() 实现进行编码,以确保订户不会为未下载的内容付费。有关其他信息,请参见 3.2.8 reverse 方法

3.1.5 订阅验证

当订户在订阅时段结束后访问内容时,将会启动订阅验证进程。图 3-5 显示了此进程。

图 3-5 订阅验证的进程流


有关订阅验证的概述。此图以文本形式进行说明。

以下各项提供了有关订阅验证进程的其他详细信息:

1. 当订户在订阅时段结束后访问内容时,Content Delivery Server 将调用 checkSubscription 方法,以查看是否在 Content Delivery Server 外部取消了订阅。

2. 对 checkSubscription 方法实现进行编码,以便在返回到 Content Delivery Server 的 BillingInfo 对象中设置 "Subscription Terminated" 标志。有关其他信息,请参见 3.2.2 checkSubscription 方法

3. 如果 BillingInfo 对象中的 "Subscription Terminated" 标志为 true,或 Content Delivery Server 的订阅状态为 canceled,订户将收到更新订阅的提示。如果该标志为 false 且状态不是 canceled,订阅将自动更新并运行该内容。

3.1.6 错误处理

当记帐系统检测到错误时,将向订户显示普通错误消息。通过在由 BillingManager 接口的实现返回的 BillingInfo 对象中设置回复消息,可以定制此消息以显示附加信息。只要记帐适配器检测到错误,即调用 setOK 方法将 BillingInfo 对象中的 "OK" 标志设置为 false,并调用 setReplyMessage 方法以设置向订户显示的消息。

3.1.7 定制字段访问

有关内容的信息存储在 Content Delivery Server 提供的数据字段中,例如,名称、说明和内容类型。可以将实现所需的其他信息存储在定义的定制字段中。可以使用 BillingInfo 对象访问有关内容的信息,包括定制字段中的信息。

当 Content Delivery Server 创建 BillingInfo 对象时,将会调用 setReplyOthers 方法,以便将定制字段添加到散列表中,该表包含特定于实现的信息。添加到表中的每个字段的键与用于定义定制字段的键相同。要从记帐管理器中访问这些字段,请调用 getReplyOthers 方法,然后使用定制字段键提取散列表中的项目。

有关设置定制字段的信息,请参见《Sun Javatrademark System Content Delivery Server 5.1 集成和配置指南》中的第 1.8 节。


3.2 BillingManager 接口

BillingManager 接口根据 BillingInfo 对象中包含的事务详细信息处理记帐事务。实现 BillingManager 以创建系统的记帐适配器。此记帐适配器是 Content Delivery Server 与记帐系统之间的接口。

3.2.1 authorize 方法

abstract BillingInfo authorize(BillingInfo inBillingInfo, boolean[] inNeedToAuthorizeBillingModel)

如果外部记帐系统需要对事务进行授权,则当订户单击项目旁边的“购买”按钮时,Content Delivery Server 将调用 authorize 方法。要表明需要授权,必须通过 getBillingInfo 方法设置 BillingInfo 对象中的 "Authorize Needed" 标志。

使用此方法可以确定订户是否已被授权购买所请求的内容。如果记帐系统支持预付记帐模型,则使用此方法可以检验订户帐户中是否有足够的余额购买该内容。

参数 inNeedToAuthorizeBillingModel 提供一个标志数组,这些标志按 BillingConstants 类中定义的以下价格模型常量进行了编索:

每个标志表示在授权事务时是否考虑价格模型。例如,假设已给定以下参数:

将按 DOWNLOAD 编索的标志设置为 false,以表明此事务不收取下载费用。因此,在授权该事务时无需考虑下载费用。根据系统的需要使用或忽略这些标志。

如果订户被授权购买内容,则可以通过在将 BillingInfo 对象返回到 Content Delivery Server 之前调用 setIsOK 方法,将 BillingInfo 对象中的 "OK" 标志设置为 true。如果未授权订户购买该内容,则将 "OK" 标志设置为 false

如果未通过 getBillingInfo 方法设置 BillingInfo 对象中的 "Confirm Needed" 标志,则可以通过调用 setConfirmNeeded 方法来设置该标志。要在将内容成功下载到设备时通知记帐系统,请将该标志设置为 true。如果不希望通知记帐系统,请将该标志设置为 false

3.2.2 checkSubscription 方法

abstract BillingInfo checkSubscription(BillingInfo inBillingInfo) throws BillingException

如果订户试图在订阅时段结束后使用内容,Content Delivery Server 将调用 checkSubscription 方法。使用此方法可以通知 Content Delivery Server 已在 Content Delivery Server 外部终止订阅。

对实现进行编码,以便通过调用 BillingInfo 对象的 setSubscriptionTerminated 方法来设置 "Subscription Terminated" 标志。要表明已终止订阅,请将该标志设置为 true。然后订户将收到更新订阅的提示。要表明订阅仍然有效,请将该标志设置为 false。然后在下一期间将自动更新此订阅。

3.2.3 confirm 方法

abstract BillingInfo confirm(BillingInfo inBillingInfo)

如果外部记帐系统要求收到下载成功的通知,Content Delivery Server 收到订户设备的下载确认后将调用 confirm 方法。要表明需要确认,必须通过 getBillingInfo 方法或 authorize 方法设置 BillingInfo 对象中的 "Confirm Needed" 标志。

确认内容已下载后,可以使用此方法执行所需的操作。例如,您可能希望仅在收到下载成功的确认后从订户帐户扣除金额。

3.2.4 contentDelete 方法

public abstract void contentDelete(BillingInfo inBillingInfo) throws BillingException

从设备中删除内容后,Content Delivery Server 将调用 contentDelete 方法。实现此方法可通知记帐系统,订户不再使用该内容。

3.2.5 getBillingInfo 方法

abstract BillingInfo getBillingInfo(BillingInfo inBillingInfo)

当订户请求项目的详细信息或购买项目时,Content Delivery Server 将调用 getBillingInfo 方法。Content Delivery Server 创建 BillingInfo 对象并将其传递给此方法,该对象包括记帐信息。

可以使用此方法根据需要修改记帐信息,并将修改后的 BillingInfo 对象返回到 Content Delivery Server。例如,要向选定订户提供折扣,请更改 Content Delivery Server 指定的价格。

对实现进行编码,以便通过调用 BillingInfo 对象的 setAuthorizeNeeded 方法来设置 "Authorize Needed" 标志。要让记帐系统验证订户是否已被授权购买选定内容,请将该标志设置为 true。如不希望对订户进行预授权,请将该标志设置为 false

如果将 "Authorize Needed" 标志设置为 false,请在此方法中设置 "Confirm Needed" 标志和 "OK" 标志。要在将内容成功下载到设备时通知记帐系统,请通过调用 setConfirmNeeded 方法将 "Confirm Needed" 标志设置为 true。如果不希望通知记帐系统,请将该标志设置为 false

要使订户能够在 "Authorize Needed" 标志为 false 时购买内容,请通过调用 setOK 方法将 "OK" 标志设置为 true。如果将 "OK" 标志设置为 false 并且 "Authorize Needed" 标志也是 false,则不允许订户下载选定内容。如果将 "Authorize Needed" 标志设置为 true,请在 authorize 方法中设置 "OK" 标志。

3.2.6 getBillingInfos 方法

abstract BillingInfo[] getBillingInfos(BillingInfo[] inBillingInfos)

当订户请求可用内容列表或 Vending Manager 管理员请求储存内容列表时,Content Delivery Server 将调用 getBillingInfos 方法。Content Delivery Server 创建 BillingInfo 对象列表并将其传递给此方法,该对象包括记帐信息。

使用此方法可以根据需要修改记帐信息,并将修改过的 BillingInfo 对象列表返回到 Content Delivery Server。例如,要提供折扣,请更改 Content Delivery Server 指定的价格。

对实现进行编码,以便通过调用列表中每个 BillingInfo 对象的 setAuthorizeNeeded 方法来设置 "Authorize Needed" 标志。要让记帐系统验证订户是否已被授权购买选定内容,请将该标志设置为 true。如不希望对订户进行预授权,请将该标志设置为 false

如果将 "Authorize Needed" 标志设置为 false,请在此方法中设置 "Confirm Needed" 标志和 "OK" 标志。要在将内容成功下载到设备时通知记帐系统,请通过调用 setConfirmNeeded 方法将 "Confirm Needed" 标志设置为 true。如果不希望通知记帐系统,请将该标志设置为 false

要使订户能够在 "Authorize Needed" 标志为 false 时购买内容,请通过调用 setOK 方法将 "OK" 标志设置为 true。如果将 "OK" 标志设置为 false 并且 "Authorize Needed" 标志也是 false,则不允许订户下载选定内容。如果将 "Authorize Needed" 标志设置为 true,请在 authorize 方法中设置 "OK" 标志。

3.2.7 refund 方法

abstract void refund(BillingInfo inBillingInfo)

当客户服务代理使用 Vending Manager 管理控制台退还订户的购买费用时,Content Delivery Server 将调用 refund 方法。Content Delivery Server 通过存储在数据库中的记帐信息为原始记帐事务创建 BillingInfo 对象,并将其传递给此方法。

使用此方法可以执行贷记订户帐户所需的操作。

3.2.8 reverse 方法

abstract void reverse(BillingInfo inBillingInfo)

当无法将内容下载到订户设备时,Content Delivery Server 将调用 reverse 方法。Content Delivery Server 通过存储在数据库中的记帐信息为原始记帐事务创建 BillingInfo 对象,并将其传递给此方法。

使用此方法可以取消记帐事务,因此订户无需为该内容付费。

3.2.9 subscribe 方法

abstract void subscribe(BillingInfo inBillingInfo)

当订户开始订阅内容时,Content Delivery Server 将调用 subscribe 方法。Content Delivery Server 通过存储在数据库中的记帐信息为原始记帐事务创建 BillingInfo 对象,并将其传递给此方法。

使用此方法可以为订户启动订阅。如果订阅重复出现,则此方法仅在订阅启动时调用一次。在每次订阅时段结束时,记帐系统必须记得向订户收取费用。如果订阅不重复出现,则在每次订户更新订阅时调用该方法。

3.2.10 unsubscribe 方法

abstract void unsubscribe(BillingInfo inBillingInfo)

当订户取消重复出现的订阅时,Content Delivery Server 将调用 unsubscribe 方法。Content Delivery Server 通过存储在数据库中的记帐信息为原始记帐事务创建 BillingInfo 对象,并将其传递给此方法。

使用此方法可以在订阅时段结束时停止自动付费。


3.3 使用记帐 API

cdsapi.jar 文件中提供了记帐 API 的类。在编译适配器时,此文件必须位于类路径中。为方便起见,$CDS_HOME/dist/cds/staging/jar 目录中提供了所有 Content Delivery Server JAR 文件的副本。在编译所创建的适配器时,请在类路径中使用此缓冲区。

使适配器可用于 Content Delivery Server 取决于所使用的应用服务器,以及是否已部署了该服务器。要使适配器可用,请执行以下操作:

1. 为适配器创建 JAR 文件。

2. 对于所有应用服务器,请将该 JAR 文件放在 $CDS_HOME/dist/cds/lib/external 目录中。

现在,该适配器将包含在所有以后的部署中。

3. 如果现有部署需要使用该适配器,请将该 JAR 文件放在每个部署的 $CDS_HOME/deployment/deployment-name/lib/external 目录中。

如果使用的是 WebLogic Server,则会为您处理类路径。

如果使用的是 Sun Java System Application Server,则会为每个部署更新类路径:

a. 在编辑 $CDS_HOME/deployment/deployment-name/sun/domains/cdsdomain/config/domain.xml 文件之前,先对其进行备份,以便从编辑期间可能引入的任何错误中恢复。

b. 编辑 domain.xml 并修改 java-config 元素,将 JAR 文件的绝对路径添加到 classpath-suffix 属性中。

c. 保存所做的更改。

4. 编辑 $CDS_HOME/deployment/deployment-name/conf 目录中的 security.config 文件:

a. 将 module.security.billingmanager 属性设置为 BillingManager 类实现的类名,例如:

module.security.billingmanager=
com.sun.content.server.billing.external.MyBillingManager

b. 保存所做的更改。

5. 重新启动任何现有部署以识别新的 JAR 文件。


3.4 样例记帐适配器

编码样例 3-1 显示了 BillingManager 的默认实现。


编码样例 3-1 样例 BillingManager 实现

package com.sun.content.server.billing.external;
 
import com.sun.content.server.billing.BillingException;
import com.sun.content.server.billing.BillingInfo;
import com.sun.content.server.billing.BillingManager;
 
/**
 * This is a sample implementation of the Billing API.
 */
public class CDSBillingManager implements BillingManager
{
  // These flags can be used to simulate responses from the billing
  //integration.
  public static final int SUCCESS = 0;
  public static final int EXCEPTION = 1;
  public static final int BILLING_EXCEPTION = 2;
  public static final int UNAUTHORIZED = 3;
  public static final int NULL = 4;
 
  // by default everything will pass through fine
  // but you can change these at runtime.
  public static int AUTHORIZE_RESPONSE = SUCCESS;
  public static int GET_BILLING_INFO_RESPONSE = SUCCESS;
  public static int GET_BILLING_INFOS_RESPONSE = SUCCESS;
  public static int CONFIRM_RESPONSE = SUCCESS;
  public static int DELETE_RESPONSE = SUCCESS;
  public static int REFUND_RESPONSE = SUCCESS;
  public static int REVERSE_RESPONSE = SUCCESS;
  public static int SUBSCRIBE_RESPONSE = SUCCESS;
  public static int UNSUBSCRIBE_RESPONSE = SUCCESS;
  public static int CHECK_SUBSCRIPTION_RESPONSE = SUCCESS;
 
  /**
   * see BillingManager#getBillingInfo(BillingInfo)
   */
  public BillingInfo getBillingInfo(BillingInfo inBillingInfo)
         throws BillingException
  {
    if (GET_BILLING_INFO_RESPONSE == NULL)
      return null;
 
    if (GET_BILLING_INFO_RESPONSE == EXCEPTION)
      throw new NullPointerException("Developer Null Pointer");
 
    if (GET_BILLING_INFO_RESPONSE == BILLING_EXCEPTION)
      throw new BillingException("Developer Billing Exception");
 
    // Set IsAuthorizeNeeded flag
    inBillingInfo.setAuthorizeNeeded(true);
    return inBillingInfo;
  }
 
  /**
   * see BillingManager#getBillingInfos(BillingInfo[])
   */
  public BillingInfo[] 
    getBillingInfos(BillingInfo[] inBillingInfos) 
    throws BillingException
  {
      for (int index = 0; index < inBillingInfos.length; index++)
    {
      // Set IsAuthorizeNeeded flag
      inBillingInfos[index].setAuthorizeNeeded(true);
    }
 
    if (GET_BILLING_INFOS_RESPONSE == NULL)
      return null;
 
    if (GET_BILLING_INFOS_RESPONSE == EXCEPTION)
      throw new NullPointerException("Testing Null Pointer");
 
    if (GET_BILLING_INFOS_RESPONSE == BILLING_EXCEPTION)
      throw new BillingException("Testing Billing Exception");
 
    return inBillingInfos;
  }
 
  /**
   * see.BillingManager#authorize(BillingInfo, boolean[])
   */
  public BillingInfo authorize(BillingInfo inBillingInfo, 
         boolean[] inNeedToAuthorizeBillingModel)
         throws BillingException
  {
    if (AUTHORIZE_RESPONSE == NULL)
      return null;
 
    if (AUTHORIZE_RESPONSE == EXCEPTION)
      throw new NullPointerException("Testing Null Pointer");
 
    if (AUTHORIZE_RESPONSE == BILLING_EXCEPTION)
     throw new BillingException("Testing Billing Exception");
 
    if (AUTHORIZE_RESPONSE == UNAUTHORIZED)
    {
      inBillingInfo.setOk(false);
      inBillingInfo.setReplyMessage("You are not authorized");
      return inBillingInfo;
    }
 
// Set IsOk and IsConfirmNeeded flags
    inBillingInfo.setConfirmNeeded(true);
    inBillingInfo.setOk(true);
 
    return inBillingInfo;
  }
 
  /**
   * seeBillingManager#confirm(BillingInfo)
   */
  public BillingInfo confirm(BillingInfo inBillingInfo)
      throws BillingException
  {
    if (CONFIRM_RESPONSE == EXCEPTION)
      throw new NullPointerException("Developer Null Pointer");
 
    if (CONFIRM_RESPONSE == BILLING_EXCEPTION)
      throw new BillingException("Developer Billing Exception");
    return inBillingInfo;
  }
 
  /**
   * see BillingManager#reverse(BillingInfo)
   */
  public void reverse(BillingInfo inBillingInfo)
      throws BillingException
  {
    if (REVERSE_RESPONSE == EXCEPTION)
      throw new NullPointerException("Developer Null Pointer");
 
    if (REVERSE_RESPONSE == BILLING_EXCEPTION)
      throw new BillingException("Developer Billing Exception");
  }
 
  /**
   * see BillingManager#refund(BillingInfo)
   */
  public void refund(BillingInfo inBillingInfo)
      throws BillingException
  {
    if (REFUND_RESPONSE == EXCEPTION)
      throw new NullPointerException("Developer Null Pointer");
 
    if (REFUND_RESPONSE == BILLING_EXCEPTION)
      throw new BillingException("Developer Billing Exception");
  }
 
  /**
   * seeBillingManager#subscribe(BillingInfo)
   */
  public void subscribe(BillingInfo inBillingInfo)
      throws BillingException
  {
    if (SUBSCRIBE_RESPONSE == EXCEPTION)
      throw new NullPointerException("Developer Null Pointer");
 
    if (SUBSCRIBE_RESPONSE == BILLING_EXCEPTION)
       throw new BillingException("Developer Billing Exception");
  }
 
  /**
   * see BillingManager#unsubscribe(BillingInfo)
   */
  public void unsubscribe(BillingInfo inBillingInfo)
      throws BillingException
  {
    if (UNSUBSCRIBE_RESPONSE == EXCEPTION)
      throw new NullPointerException("Developer Null Pointer");
 
    if (UNSUBSCRIBE_RESPONSE == BILLING_EXCEPTION)
      throw new BillingException("Developer Billing Exception");
  }
 
  /**
   * see BillingManager#checkSubscription(BillingInfo)
   */
  public BillingInfo checkSubscription(BillingInfo inBillingInfo)
      throws BillingException
  {
    if (CHECK_SUBSCRIPTION_RESPONSE == NULL)
      return null;
 
    if (CHECK_SUBSCRIPTION_RESPONSE == EXCEPTION)
      throw new NullPointerException("Developer Null Pointer");
 
    if (CHECK_SUBSCRIPTION_RESPONSE == BILLING_EXCEPTION)
      throw new BillingException("Developer Billing Exception");
 
    inBillingInfo.setSubscriptionTerminated(false);
 
    return inBillingInfo;
  }
 
  /**
   * see BillingManager#contentDelete(BillingInfo)
   */
  public void contentDelete(BillingInfo inBillingInfo)
      throws BillingException
  {
    if (DELETE_RESPONSE == EXCEPTION)
     throw new NullPointerException("Developer Null Pointer");
 
    if (DELETE_RESPONSE == BILLING_EXCEPTION)
     throw new BillingException("Developer Billing Exception");
  }
}