v1.0.1 2015-01-31 14:06 UTC

README

这个用PHP编写的库将允许您轻松地集成PayU API v2.1
支持PHP版本 >=5.4 和 HHVM。
Build Status

通过composer安装

要安装此库,只需在composer中添加要求。
您可以通过以下方式实现

php /path/to/composer.phar require "krzysztof-gzocha/payu:v1.0.1"

或简单地将其添加到composer.json中

{
    "require": {
        "krzysztof-gzocha/payu": "v1.0.1"
    }
}

并运行

composer install

基本用法

1. 配置

默认配置对象是 \Team3\PayU\Configuration\Configuration,但任何实现 \Team3\PayU\Configuration\ConfigurationInterface 的对象都可以完成这项工作。大多数配置参数已经定义,您只需设置从PayU获取的凭据(商户ID和私钥)。测试账户凭据已在 \Team3\PayU\Configuration\Credentials\TestCredentials 中定义,因此如果您只想测试应用程序,则可以使用此类。

使用测试凭据进行配置

use Team3\PayU\Configuration\Configuration;
use Team3\PayU\Configuration\Credentials\TestCredentials;

$payuConfiguration = new Configuration(
    new TestCredentials()
);

使用真实凭据进行配置

use Team3\PayU\Configuration\Configuration;
use Team3\PayU\Configuration\Credentials\Credentials;

$payuConfiguration = new Configuration(
    new Credentials('<merchant pos id>', '<private key>')
);

2. 创建基本订单对象

请注意,这些示例中创建的订单配置不足。一些必需的参数(如OpenPayU-Signature、totalAmount、customerIp等)仍然缺失。您可以自动添加它们,但这将在另一章中描述。这两个示例都描述了最基本的配置。由这两个示例创建的订单是相同的。

2.1 使用库中的订单对象

此库中的订单对象默认表示为 \Team3\PayU\Order\Model\Order,但任何实现 \Team3\PayU\Order\Model\OrderInterface 的对象都可以使用。本章是关于创建此对象,但如果您正在将此库与当前正在运行的应用程序集成,则可以使用下一章中描述的注解自动执行此操作。

示例订单(单个产品)

use \Team3\PayU\Order\Model\Order;
use \Team3\PayU\Order\Model\Products\Product;
use \Team3\PayU\Order\Model\Money\Money;

$order = new Order();
$product = new Product();

$order
    ->setDescription('Example order')
    ->setCurrencyCode('EUR')
    ->setOrderId('123456');

$product
    ->setName('Some product')
    ->setQuantity(10)
    ->setUnitPrice(new Money(10));

$order->getProductCollection()->addProduct($product);
2.2 使用注解

如果您已经在应用程序中有一个订单对象,则不需要创建另一个订单对象。您只需在返回有用参数的 方法 上放置单个注解 \Team3\PayU\Annotation\PayU。假设您有一个自己的订单类称为 \Users\App\UserOrder 和产品类称为 \Users\App\UserProduct。注解可以设置在公共、受保护或私有方法上。您可以按以下方式添加注解

namespace Users\App;

use Team3\PayU\Annotation\PayU;
use Team3\PayU\Order\Model\Money\Money;

class UserOrder
{
	/**
	* @PayU(propertyName="general.orderId")
	*/
	public function getId()
	{
		return '123456';
	}

	/**
	* @PayU(propertyName="general.description")
	*/
	private function getDescription()
	{
		return 'Example order';
	}

	/**
	* @PayU(propertyName="general.currencyCode")
	*/
	private function getCurrencyCode()
	{
		return 'EUR';
	}

	/**
	* @PayU(propertyName="productCollection")
	*/
	private function getProducts()
	{
		// Both array and \Traversable object can be returned
		return [
			new UserProduct(),
		];
	}
}

class UserProduct
{
	/**
	* @PayU(propertyName="product.name")
	*/
	private function getName()
	{
		return 'Some product';
	}

	/**
	* @PayU(propertyName="product.quantity")
	*/
	private function getQuantity()
	{
		return 10;
	}
	
	/**
	* @PayU(propertyName="product.unitPrice")
	*/
	private function getPrice()
	{
		// This method should return anything
		// that implements \Team3\PayU\Order\Model\Money\MoneyInterface
		return new Money(10);
	} 
}

所有属性名称都可以在 \Team3\PayU\Order\Transformer\UserOrder\TransformerProperties 中找到。值得一提的是,其中一个注解特别适用于复杂的架构。它称为 follow 注解。如果您必须从不同的、相互连接的实体中收集所需的参数,则可以使用此注解。此方法的结果将通过此转换器再次传递。

现在,要创建订单对象,您必须使用UserOrderTransfomer,该转换器将实际转换您的对象以供库使用。为此,您可以按照以下示例操作

use \Team3\PayU\Order\Transformer\UserOrder\UserOrderTransformerFactory;

$order = new Order(); // Order for library purposes
$userOrder = new UserOrder(); // Order from users application

$logger = new NullLogger(); // ONLY for example. Use real logger.
$transformerFactory = new UserOrderTransformerFactory();
$transformer = $transformerFactory->build($logger);

// Will transform UserOrder into Order
$transformer->transform($order, $userOrder);

// $order->getDescription() => 'Example order'

3. 订单参数自动完成

在提供其他必需参数(如签名)方面,您不会孤立无援。为此,您可以使用此代码

use \Team3\PayU\NullLogger;
use \Team3\PayU\Order\Autocomplete\OrderAutocompleteFactory;

$logger = new NullLogger(); // ONLY for example. Use real logger.
$autocompleteFactory = new OrderAutocompleteFactory();
$autocomplete = $autocompleteFactory->build($logger);

// Complete $order with parameters. Use $credentials
try {
	$autocomplete->autocomplete($order, $credentials);
} catch (OrderAutocompleteException $exception) {
	// Something went wrong.
}

// $order->getSignature() => '7f46165474d11ee5836777d85df2cdab';

4. 通知PayU关于新订单

要通知PayU关于我们的新订单,您必须发送OrderCreateRequest并使用正确构建的对象,检查响应的HTTP状态码,将响应反序列化为OrderCreateResponse对象,检查请求状态是否为SUCCESS,并将用户重定向到指定的URL。以下是如何使用RequestProcess的示例

use \Symfony\Component\Validator\ConstraintViolationListInterface;
use \Team3\PayU\Communication\Process\RequestProcessFactory;
use \Team3\PayU\Communication\Request\OrderCreateRequest;
use \Team3\PayU\Communication\Response\OrderCreateResponse;
use \Team3\PayU\NullLogger;

$logger = new NullLogger(); // ONLY for example. Use real logger.
$requestProcessFactory = new RequestProcessFactory();
$requestProcess = $requestProcessFactory->build($logger);

try {
	/** @var OrderCreateResponse $orderCreateResponse **/
	$orderCreateResponse = $requestProcess->process(
	    new OrderCreateRequest($order),
	    $configuration
	);
} catch (InvalidRequestDataObjectException $exception) {
	/** 
	* $order is invalid. Violations are stored in exception.
	* @var ConstraintViolationListInterface $violations 
	*/
	$violations = $exception->getViolations();
} catch (PayUException $exception) {
	// something went wrong.
}

if ($orderCreateResponse->getRequestStatus()->isSuccess()) {
	// Request was ok. You can redirect user to given url 
	$this->redirectTo(
		$orderCreateResponse->getRedirectUri()
	);
} else {
	// Request was not ok. 
	// Pass this information to user however you want
}

5. 从PayU检索订单

获取信息的过程与创建新订单类似。您可以使用相同的RequestProcess,但需要使用不同的Request对象。示例:

// $requestProcess was created in exactly the same way.
use \Team3\PayU\Communication\Response\OrderRetrieveResponse;

$order->setPayUOrderId('<order id from payu>');
$requestProcess->shouldValidate(false); // We dont need to validate this time

try {
	/** @var OrderRetrieveResponse $orderStatusResponse */
	$orderStatusResponse = $requestProcess->process(
		new OrderRetrieveRequest($order), // $order->getPayUOrderId() should not be null
		$configuration
	);
} catch (PayUException $exception) {
	// Something went wrong..
}

// Order status:
// $status = $orderStatusResponse->getFirstOrder()->getStatus();
// Completed status:
// $status->isCompleted() -> true

6. 订单取消请求

您可以向PayU发送请求,表示“取消此订单”。为此,您可以使用上面描述的RequestProcess。如果您向它传递一个对象Team3\Communication\Request\OrderCancelRequest,并用OrderInterfacePayUOrderId参数初始化它,那么您将取消订单。在这种情况下,RequestProcess将返回Team3\Communication\Response\OrderCancelResponse。您可以在文档中查看其参数。请注意,参数status请求的状态,而不是订单的状态!

7. 处理订单通知

PayU可以通知您订单的任何更改。要使用此机制,您只需定义$order->setNotifyUrl('<您的应用中的URL>')。PayU将直接将通知发送到该URL。在notify URL中定义的操作中,您需要解析通知的JSON字符串并检查订单状态。为此,您可以使用NotificationProcess。

use \Team3\PayU\Communication\Process\NotificationProcess\NotificationProcessFactory;
use \Team3\PayU\Communication\Notification\OrderNotification;
use \Team3\PayU\Communication\Process\NotificationProcess\NotificationProcessException;

$logger = new NullLogger(); // Only for example. 
$notificationProcessFactory = new NotificationProcessFactory();
$notificationProcess = $notificationProcessFactory->build($logger);

// $notificationData is content of received notification  
// $signatureHeader can be read from http header "OpenPayu-Signature"  
// from received notification. It can be null.  

try {
	/** @var OrderNotification $orderNotification */
	$orderNotification = $notificationProcess->process(
		$configuration->getCredentials(),
		$notificationData,
		$signatureHeader
	);
} catch (NotificationProcessException $exception) {
	// Something was wrong with the process. Maybe signature was wrong?
} catch (PayUException $exception) {
	// Something went really wrong..
}

// $orderNotification->getOrder()->getStatus->isCompleted() -> true

重要

请注意,PayU将以异步方式发送通知,因此当您收到关于订单完成或取消的通知时,您应该忽略所有后续通知。

运行测试

要本地运行测试,您必须使用--require-dev参数通过composer安装库。这将安装Codeception框架,它有助于单元测试。Codeception使用辅助工具,但它们不在这个库中。您可以使用./bin/codecept build构建它们 - 现在您已准备好。现在您可以使用./bin/codecept run unit运行单元测试,或使用./bin/codecept run unit -c codeception.yml --coverage --coverage-html --coverage-xml生成代码覆盖率报告。此库是使用Phing构建的,因此您可以使用phing unit-testsphing coverage

实际代码覆盖率报告可在krzysztof-gzocha.github.io/payu/coverage找到。

贡献

所有pull请求都受到欢迎和赞赏 :)

许可

MIT许可证在license.txt中可用。