Mở đầu
Ở thời đại công nghệ phát triển như bây giờ, shopping online đang cực kỳ phát triển. Ta có thể khẳng định điều đó hơn nữa khi đại dịch covid-19 càn quét, khối tài sản của ông chủ amazone đã tăng thêm tới 70 tỷ đô. Lý do là mọi người mua sắm online nhiều hơn khi cách ly, mà mua sắm online thì cần có 1 cổng giao dịch đáng tin cậy thay vì ship COD có thể bị bùng đơn hàng gây mất thời gian và tiền bạc cho người kinh doanh. Sau đây mình xin giới thiệu về gem active_merchant sẽ giúp chúng ta thanh toán 1 cách đơn giản. Gem được support bởi nền tảng ecommerce cực kỳ lớn shopify, và nhà phát triển của CMS ecommerce cũng khá lớn đó là Spreedly.
1. Cài đặt
Mở cmd và tạo project nhé.
- Tạo project
1 2 | rails <span class="token keyword">new</span> <span class="token class-name">app</span> ecommerce_with_active_merchant |
- Cài đặt gem
1 2 3 4 | cd ecommerce_with_active_merchant gem install activemerchant |
- Sau đó các bạn nhớ thêm vào gem file nhé
1 2 | gem <span class="token string">'activemerchant'</span> |
- Tạo model
1 2 3 4 | rails g scaffold order <span class="token keyword">new</span> cart_id<span class="token symbol">:integer</span> ip_address<span class="token symbol">:string</span> first_name<span class="token symbol">:string</span> last_name<span class="token symbol">:string</span> card_type<span class="token symbol">:string</span> card_expires_on<span class="token symbol">:date</span> rails g model order_transaction order_id<span class="token symbol">:integer</span> action<span class="token symbol">:string</span> amount<span class="token symbol">:integer</span> success<span class="token symbol">:boolean</span> authorization<span class="token symbol">:string</span> message<span class="token symbol">:string</span> params<span class="token symbol">:text</span> |
- Config development
1 2 3 4 5 6 7 8 9 | config<span class="token punctuation">.</span>after_initialize <span class="token keyword">do</span> <span class="token constant">ActiveMerchant</span><span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token constant">Billing</span><span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token constant">Base</span><span class="token punctuation">.</span>mode <span class="token operator">=</span> <span class="token symbol">:test</span> <span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token constant">GATEWAY</span> <span class="token operator">=</span> <span class="token constant">ActiveMerchant</span><span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token constant">Billing</span><span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token constant">PaypalGateway</span><span class="token punctuation">.</span><span class="token keyword">new</span><span class="token punctuation">(</span> <span class="token symbol">:login</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token string">"seller_1229899173_biz_api1.railscasts.com"</span><span class="token punctuation">,</span> <span class="token symbol">:password</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token string">"FXWU58S7KXFC6HBE"</span><span class="token punctuation">,</span> <span class="token symbol">:signature</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token string">"AGjv6SW.mTiKxtkm6L9DcSUCUgePAUDQ3L-kTdszkPG8mRfjaRZDYtSu"</span> <span class="token punctuation">)</span> <span class="token keyword">end</span> |
- Config test
1 2 3 4 5 | config<span class="token punctuation">.</span>after_initialize <span class="token keyword">do</span> <span class="token constant">ActiveMerchant</span><span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token constant">Billing</span><span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token constant">Base</span><span class="token punctuation">.</span>mode <span class="token operator">=</span> <span class="token symbol">:test</span> <span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token constant">GATEWAY</span> <span class="token operator">=</span> <span class="token constant">ActiveMerchant</span><span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token constant">Billing</span><span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token constant">BogusGateway</span><span class="token punctuation">.</span><span class="token keyword">new</span> <span class="token keyword">end</span> |
- Config production
1 2 3 4 5 6 7 8 9 | config<span class="token punctuation">.</span>after_initialize <span class="token keyword">do</span> <span class="token constant">ActiveMerchant</span><span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token constant">Billing</span><span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token constant">Base</span><span class="token punctuation">.</span>mode <span class="token operator">=</span> <span class="token symbol">:production</span> <span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token constant">GATEWAY</span> <span class="token operator">=</span> <span class="token constant">ActiveMerchant</span><span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token constant">Billing</span><span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token constant">PaypalGateway</span><span class="token punctuation">.</span><span class="token keyword">new</span><span class="token punctuation">(</span> <span class="token symbol">:login</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token string">"seller_1229899173_biz_api1.railscasts.com"</span><span class="token punctuation">,</span> <span class="token symbol">:password</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token string">"FXWU58S7KXFC6HBE"</span><span class="token punctuation">,</span> <span class="token symbol">:signature</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token string">"AGjv6SW.mTiKxtkm6L9DcSUCUgePAUDQ3L-kTdszkPG8mRfjaRZDYtSu"</span> <span class="token punctuation">)</span> <span class="token keyword">end</span> |
- Ta thêm action xử lý create trong controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <span class="token keyword">def</span> <span class="token method-definition"><span class="token function">create</span></span> <span class="token variable">@order</span> <span class="token operator">=</span> current_cart<span class="token punctuation">.</span>build_order<span class="token punctuation">(</span>params<span class="token punctuation">[</span><span class="token symbol">:order</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token variable">@order</span><span class="token punctuation">.</span>ip_address <span class="token operator">=</span> request<span class="token punctuation">.</span>remote_ip <span class="token keyword">if</span> <span class="token variable">@order</span><span class="token punctuation">.</span>save <span class="token keyword">if</span> <span class="token variable">@order</span><span class="token punctuation">.</span>purchase render <span class="token symbol">:action</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token string">"success"</span> <span class="token keyword">else</span> render <span class="token symbol">:action</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token string">"failure"</span> <span class="token keyword">end</span> <span class="token keyword">else</span> render <span class="token symbol">:action</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token string">'new'</span> <span class="token keyword">end</span> <span class="token keyword">end</span> |
- Tạo model cart.rb
1 2 | rails g model cart |
- Vì mỗi cart sẽ ứng với 1 order nên ta thêm relation
1 2 3 4 | <span class="token keyword">class</span> <span class="token class-name">Cart</span> <span class="token operator"><</span> <span class="token constant">ApplicationRecord</span> has_one <span class="token symbol">:order</span> <span class="token keyword">end</span> |
- Chỉnh sửa model order
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | <span class="token keyword">class</span> <span class="token class-name">Order</span> <span class="token operator"><</span> <span class="token constant">ApplicationRecord</span> belongs_to <span class="token symbol">:cart</span> has_many <span class="token symbol">:transactions</span><span class="token punctuation">,</span> class_name<span class="token punctuation">:</span> <span class="token string">"OrderTransaction"</span> attr_accessor <span class="token symbol">:card_number</span><span class="token punctuation">,</span> <span class="token symbol">:card_verification</span> validate_on_create <span class="token symbol">:validate_card</span> <span class="token keyword">def</span> <span class="token method-definition"><span class="token function">purchase</span></span> response <span class="token operator">=</span> <span class="token constant">GATEWAY</span><span class="token punctuation">.</span>purchase<span class="token punctuation">(</span>price_in_cents<span class="token punctuation">,</span> credit_card<span class="token punctuation">,</span> purchase_options<span class="token punctuation">)</span> transactions<span class="token punctuation">.</span>create<span class="token operator">!</span><span class="token punctuation">(</span><span class="token symbol">:action</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token string">"purchase"</span><span class="token punctuation">,</span> <span class="token symbol">:amount</span> <span class="token operator">=</span><span class="token operator">></span> price_in_cents<span class="token punctuation">,</span> <span class="token symbol">:response</span> <span class="token operator">=</span><span class="token operator">></span> response<span class="token punctuation">)</span> cart<span class="token punctuation">.</span>update_attribute<span class="token punctuation">(</span><span class="token symbol">:purchased_at</span><span class="token punctuation">,</span> <span class="token builtin">Time</span><span class="token punctuation">.</span>now<span class="token punctuation">)</span> <span class="token keyword">if</span> response<span class="token punctuation">.</span>success<span class="token operator">?</span> response<span class="token punctuation">.</span>success<span class="token operator">?</span> <span class="token keyword">end</span> <span class="token keyword">def</span> <span class="token method-definition"><span class="token function">price_in_cents</span></span> <span class="token punctuation">(</span>cart<span class="token punctuation">.</span>total_price<span class="token operator">*</span><span class="token number">100</span><span class="token punctuation">)</span><span class="token punctuation">.</span>round <span class="token keyword">end</span> <span class="token keyword">private</span> <span class="token keyword">def</span> <span class="token method-definition"><span class="token function">purchase_options</span></span> <span class="token punctuation">{</span> <span class="token symbol">:ip</span> <span class="token operator">=</span><span class="token operator">></span> ip_address<span class="token punctuation">,</span> <span class="token symbol">:billing_address</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token punctuation">{</span> <span class="token symbol">:name</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token string">"Sun Staff"</span><span class="token punctuation">,</span> <span class="token symbol">:address1</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token string">"13F KNE Pham Hung street"</span><span class="token punctuation">,</span> <span class="token symbol">:city</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token string">"HaNoi"</span><span class="token punctuation">,</span> <span class="token symbol">:state</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token string">"HN"</span><span class="token punctuation">,</span> <span class="token symbol">:country</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token string">"VI"</span><span class="token punctuation">,</span> <span class="token symbol">:zip</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token string">"100000"</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">end</span> <span class="token keyword">def</span> <span class="token method-definition"><span class="token function">validate_card</span></span> <span class="token keyword">unless</span> credit_card<span class="token punctuation">.</span>valid<span class="token operator">?</span> credit_card<span class="token punctuation">.</span>errors<span class="token punctuation">.</span>full_messages<span class="token punctuation">.</span><span class="token keyword">each</span> <span class="token keyword">do</span> <span class="token operator">|</span>message<span class="token operator">|</span> errors<span class="token punctuation">.</span>add_to_base message <span class="token keyword">end</span> <span class="token keyword">end</span> <span class="token keyword">end</span> <span class="token keyword">def</span> <span class="token method-definition"><span class="token function">credit_card</span></span> <span class="token variable">@credit_card</span> <span class="token operator">||</span><span class="token operator">=</span> <span class="token constant">ActiveMerchant</span><span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token constant">Billing</span><span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token constant">CreditCard</span><span class="token punctuation">.</span><span class="token keyword">new</span><span class="token punctuation">(</span> <span class="token symbol">:type</span> <span class="token operator">=</span><span class="token operator">></span> card_type<span class="token punctuation">,</span> <span class="token symbol">:number</span> <span class="token operator">=</span><span class="token operator">></span> card_number<span class="token punctuation">,</span> <span class="token symbol">:verification_value</span> <span class="token operator">=</span><span class="token operator">></span> card_verification<span class="token punctuation">,</span> <span class="token symbol">:month</span> <span class="token operator">=</span><span class="token operator">></span> card_expires_on<span class="token punctuation">.</span>month<span class="token punctuation">,</span> <span class="token symbol">:year</span> <span class="token operator">=</span><span class="token operator">></span> card_expires_on<span class="token punctuation">.</span>year<span class="token punctuation">,</span> <span class="token symbol">:first_name</span> <span class="token operator">=</span><span class="token operator">></span> first_name<span class="token punctuation">,</span> <span class="token symbol">:last_name</span> <span class="token operator">=</span><span class="token operator">></span> last_name <span class="token punctuation">)</span> <span class="token keyword">end</span> <span class="token keyword">end</span> |
- Transaction cho order
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <span class="token keyword">class</span> <span class="token class-name">OrderTransaction</span> <span class="token operator"><</span> <span class="token constant">ApplicationRecord</span> belongs_to <span class="token symbol">:order</span> serialize <span class="token symbol">:params</span> <span class="token keyword">def</span> <span class="token method-definition"><span class="token function">response</span></span><span class="token operator">=</span><span class="token punctuation">(</span>response<span class="token punctuation">)</span> <span class="token keyword">self</span><span class="token punctuation">.</span>success <span class="token operator">=</span> response<span class="token punctuation">.</span>success<span class="token operator">?</span> <span class="token keyword">self</span><span class="token punctuation">.</span>authorization <span class="token operator">=</span> response<span class="token punctuation">.</span>authorization <span class="token keyword">self</span><span class="token punctuation">.</span>message <span class="token operator">=</span> response<span class="token punctuation">.</span>message <span class="token keyword">self</span><span class="token punctuation">.</span>params <span class="token operator">=</span> response<span class="token punctuation">.</span>params <span class="token keyword">rescue</span> <span class="token constant">ActiveMerchant</span><span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token constant">ActiveMerchantError</span> <span class="token operator">=</span><span class="token operator">></span> e <span class="token keyword">self</span><span class="token punctuation">.</span>success <span class="token operator">=</span> <span class="token boolean">false</span> <span class="token keyword">self</span><span class="token punctuation">.</span>authorization <span class="token operator">=</span> <span class="token keyword">nil</span> <span class="token keyword">self</span><span class="token punctuation">.</span>message <span class="token operator">=</span> e<span class="token punctuation">.</span>message <span class="token keyword">self</span><span class="token punctuation">.</span>params <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token keyword">end</span> <span class="token keyword">end</span> |
- Chỉnh sửa form hiển thị thanh toán
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | <span class="token operator"><</span><span class="token string">%= form_for @order do |f| %> <%=</span> f<span class="token punctuation">.</span>error_messages <span class="token string">%> <p></span> <span class="token operator"><</span><span class="token string">%= f.label :first_name %><br /> <%=</span> f<span class="token punctuation">.</span>text_field <span class="token symbol">:first_name</span> <span class="token string">%> </p></span> <span class="token operator"><</span>p<span class="token operator">></span> <span class="token operator"><</span><span class="token string">%= f.label :last_name %><br /> <%=</span> f<span class="token punctuation">.</span>text_field <span class="token symbol">:last_name</span> <span class="token string">%> </p></span> <span class="token operator"><</span>p<span class="token operator">></span> <span class="token operator"><</span><span class="token string">%= f.label :card_type %><br /> <%=</span> f<span class="token punctuation">.</span>select <span class="token symbol">:card_type</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token punctuation">[</span><span class="token string">"Visa"</span><span class="token punctuation">,</span> <span class="token string">"visa"</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token string">"MasterCard"</span><span class="token punctuation">,</span> <span class="token string">"master"</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token string">"Discover"</span><span class="token punctuation">,</span> <span class="token string">"discover"</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token string">"American Express"</span><span class="token punctuation">,</span> <span class="token string">"american_express"</span><span class="token punctuation">]</span><span class="token punctuation">]</span> <span class="token string">%> </p></span> <span class="token operator"><</span>p<span class="token operator">></span> <span class="token operator"><</span><span class="token string">%= f.label :card_number %><br /> <%=</span> f<span class="token punctuation">.</span>text_field <span class="token symbol">:card_number</span> <span class="token string">%> </p></span> <span class="token operator"><</span>p<span class="token operator">></span> <span class="token operator"><</span><span class="token string">%= f.label :card_verification, "Card Verification Value (CVV)" %><br /> <%=</span> f<span class="token punctuation">.</span>text_field <span class="token symbol">:card_verification</span> <span class="token string">%> </p></span> <span class="token operator"><</span>p<span class="token operator">></span> <span class="token operator"><</span><span class="token string">%= f.label :card_expires_on %><br /> <%=</span> f<span class="token punctuation">.</span>date_select <span class="token symbol">:card_expires_on</span><span class="token punctuation">,</span> <span class="token symbol">:discard_day</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token boolean">true</span><span class="token punctuation">,</span> <span class="token symbol">:start_year</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token constant">Date</span><span class="token punctuation">.</span>today<span class="token punctuation">.</span>year<span class="token punctuation">,</span> <span class="token symbol">:end_year</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token punctuation">(</span><span class="token constant">Date</span><span class="token punctuation">.</span>today<span class="token punctuation">.</span>year<span class="token operator">+</span><span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token symbol">:add_month_numbers</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token boolean">true</span> <span class="token string">%> </p></span> <span class="token operator"><</span>p<span class="token operator">></span><span class="token operator"><</span><span class="token operator">%</span><span class="token operator">=</span> f<span class="token punctuation">.</span>submit <span class="token string">"Submit"</span> <span class="token string">%></p></span> <span class="token operator"><</span><span class="token operator">%</span> <span class="token keyword">end</span> <span class="token operator">%</span><span class="token operator">></span> |
- Thêm action và view tương ứng cho trạng thái xử lý order
1 2 3 | <span class="token keyword">def</span> <span class="token method-definition"><span class="token function">success</span></span><span class="token punctuation">;</span> <span class="token keyword">end</span> <span class="token keyword">def</span> <span class="token method-definition"><span class="token function">failure</span></span><span class="token punctuation">;</span> <span class="token keyword">end</span> |
1 2 | <span class="token constant">SUCCESS</span> |
1 2 | <span class="token constant">FAILED</span> |
Mirgate database rồi rails s để xem thành quả nhé.
create order view