PSCB Integration
PSCB bank payment service integration (trade acquiring).
Official documentation http://docs.pscb.ru/oos/
Offer https://pscb.ru/corp/services/payment_service/platezhi-v-internete/.
Disclamer
Stay awhile and listen. Are you sure that you need this stuff?
Pro:
- 3% fee for Visa/MasterCard payments
Cons:
- During 1.5 years of our expirience PSCB lost all client's reccurent bingings twice. We lost a lot of revenue because of this.
- Very slow personal account web site, it takes tens of seconds to load a page.
- API looks not solid and have a lack of consistency, it has several ways to return errors in response
- PSCB send demo environment payment callbacks to your production server, and if you don't handle them, they send you email like 'We have got invalid response for our HTTP-callbacks', so they can't completely split demo and production environments
Your choise
It's up to you. Probably Yandex.Kassa will be better option, its API looks much more solid and reliable (it's Yandex anyway) but it has higher fee 3.5% on base plan for bank cards. But if you revenue more than 1 million RUB per month then only 2.8%. I suppose the choice is obvious here.
Installation
I see you decided to try. Good luck and best wishes.
Add this line to your application's Gemfile:
gem 'pscb_integration'And then execute:
$ bundle
Usage
Configuration
Configure it in <you app folder>/config/initializers/pscb.rb with:
PscbIntegration.setup do |config|
config.host = 'https://oos.pscb.ru'
config.market_place = '<your market place id>'
config.secret_key = '<your secret key>'
config.demo_secret_key = '<your secret key for demo env>'
config.confirm_payment_callback = PaymentService.method(:confirm_pscb_payment_callback)
endIf your application didn't setup PscbIntegration configuration with setup block then PscbIntegration::ConfigurationError will be raised during first attempt to call of any method.
Handling payment status notification
Mount engine in your routes.rb file as you wish, e.g.:
namespace :integration_api do
mount PscbIntegration::Engine => '/'
endImplement callback function assigned to confirm_pscb_payment_callback:
Arguments:
payment - hash with payment details from PSCB:
| Property | Description |
|---|---|
orderId |
Unique order id generated by merchant |
showOrderId |
Not uniqe order id generated by merchant to show it to customer |
paymentId |
Order id generated by PSCB |
account |
Customer id on merchant side |
marketPlace |
Merchant id on PSCB side |
paymentMethod |
Payment method |
state |
Payment state |
stateDate |
Date of last state changing ISO8601 |
amount |
Order amount |
recurrencyToken |
Recurrency token |
is_demo - if true then payment is from demo environment else from production.
Callback should return true if you system accepts and confirms payment, and false (nil) in case of rejecting. Example:
def confirm_pscb_payment_callback(payment, is_demo)
if (order = OrderModel.find_by(uid: payment['orderId']))
# Some state machine transition
order.apply_status(payment['state'])
end
endBuild payment url
client = PscbIntegration::Client.new
url = client.build_payment_url(
nonce: SecureRandom.hex(5), # Salt to avoid replay attack
customerAccount: user.id, # Some user id
customerRating: 5, # Customer rating
customerEmail: user.email, # Customer email
customerPhone: user.phone, # Customer phone
orderId: '123456', # Unique order id
details: 'Some paymnet', # Payment details comment
amount: 500, # Amount in RUB
paymentMethod: 'ac', # Payment menthod
recurrentable: true, # Payment can be repeated by merchant
data: {
debug: 1, # show debug info in customer browser
}
)Pull order status
Client methods return result in Either monad for helping handling errors on different level. Thank you @bolshakov.
How it works:
-
Rightresult means success -
Leftresult means PSCB returns some conscious error which can require special handling on our side. - any exception means unexpected error (e.g. timeout, network) that we don't know how to handle, and probably best option is to log it and try again later.
Learn more about Either monad usage. Example:
client = PscbIntegration::Client.new
res = client.pull_order_status(order.id)
res.reduce(
# Left result is handled here
# @param error - PscbIntegration::BaseApiError
->(error) {
# Some special error handling e.g.
if error.unknown_payment?
# Do something special
end
},
# Right result is handled here
# @param payment - payment hash from PSCB
->(payment) {
# Update order status
}
)Recurring payment
Before this call you customer should successefully paid order with recurrentable flag. In callback or through status pulling recurrency_token will be returned.
client = PscbIntegration::Client.new
res = client.recurring_payment(
prev_order_uid: prev_order.id, # Previous recurrentable order id
new_order_uid: new_order.id, # New order id
token: recurrency_token, # Recurrency token from previous order
amount: 300, # Amount in RUB
)
res.reduce(
->(error) { },
->(payment) { },
)Refund order
client = PscbIntegration::Client.new
res = client.refund_order(order.id)
res.reduce(
->(error) { },
->(payment) { },
)Development
After checking out the repo, run bin/setup to install dependencies. Then, run rake spec to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and tags, and push the .gem file to rubygems.org.
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/holyketzer/pscb_integration.
License
The gem is available as open source under the terms of the MIT License.