今天做完就差不多串接好金流了!
還記得我們在設定 info 參數的時候,有個叫做 ReturnURL
的參數嗎
這個參數會在消費者付款成功的時候,導回我們的網站(但如果你想導別的網站也是行)
但藍新禁止 ReturnURL 為 127 開頭
我們可以用 ngrok 來做
安裝及設定可以參考官方網站
官方寫得很清楚且很容易做
不過我們在 Rails 還需要多設定
Rails 為了防止 DNS 攻擊,所以多做了一層防護
我們要設定哪些網域可以存取專案
config.hosts << "ngrok 的網域"
設定完畢後再重開 server
接下來我們就要繼續設定金流了
# app/service/newebpay.rb
def set_info(order)
...
info[:ReturnURL] = "ngrok 網域/orders/newebpay_return"
...
end
CSRF 是一種攻擊,用惡意連結或者網站來侵害使用者,
為了防止這種攻擊,Rails 使用 authenticity token 來保護
藍新導回網站的時候,因為是跨網域,不會有正確的 authenticity token
所以需要去 skip 這個保護機制
class OrdersController < ApplicationController
skip_before_action :verify_authenticity_token, only: [:newebpay_return]
def newebpay_return
end
end
當消費者付款成功時,藍新會傳一包資料回來,不過那包資料也是加密過的
所以我們必須將它解密,並且把部分資料存到資料庫當中
先來做初始化的設定
一樣需要 Hash Key 以及 Hash IV
接著要來將 params (藍新傳過來的那包) 解密
# app/service/newebpay_result.rb
class NewebpayResult
def initialize(params)
@key = ENV["HASH_KEY"]
@iv = ENV["HASH_IV"]
response = decrypy(params)
end
end
根據官方手冊說明,藍新會回傳幾個資料
Status
付款成功與否MerchantID
商店 IDTradeInfo
交易資料 (以 AES 加密)TradeSha
交易資料 (以 TradeInfo 再用 SHA256 加密)
我們需要的資料應該都放在 TradeInfo 的 Result 中
現在就來解密吧
# app/service/newebpay_result.rb
class NewebpayResult
def decrypy(raw_data)
# 將十六進位轉換為二進位
encrypted_data = [raw_data].pack('H*')
# 用 AES 方式解除雜湊
decipher = OpenSSL::Cipher::AES256.new(:CBC)
# 設定 decipher 為解密模式
decipher.decrypt
# 加解密模式中的一種為了滿足資料長度的填充技術
decipher.padding = 0
# 將 @key 及 @iv 塞進 decipher
decipher.key = @key
decipher.iv = @iv
# 開始解密
data = decipher.update(encrypted_data) + decipher.final
# 移除數據的填充
plain = data.strip
if plain[-1] != '}'
plain = plain[0, plain.index(plain[-1])]
end
# 返回資料
JSON.parse(plain)
end
end
初始化返回 response
# app/service/newebpay_result.rb
class NewebpayResult
attr_reader :response
def initialize(params)
@key = ENV["HASH_KEY"]
@iv = ENV["HASH_IV"]
@response = decrypy(params)
end
end
controller 接下 response 並且做出 payment
# app/controllers/orders_controller.rb
class OrdersController < ApplicationController
def newebpay_return
response = NewebpayResult.new(params[:TradeInfo]).response
if response["Status"] != "SUCCESS"
redirect_to root_path, notice: '付款失敗'
else
response = response["Result"]
order = Order.find_by(slug: response["MerchantOrderNo"])
user = order.user
@payment = Payment.create(order:, user:, amount: response["Amt"], paid_at: response["PayTime"], trade_no: response["TradeNo"])
end
end
以上就差不多串好金流了,如果要做更細緻的話,各位可以再查藍新的手冊