pay-lago 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/workflows/main.yml +27 -0
- data/.gitignore +28 -0
- data/.rspec +3 -0
- data/.standard.yml +3 -0
- data/CHANGELOG.md +2 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +25 -0
- data/Gemfile.lock +292 -0
- data/LICENSE.txt +21 -0
- data/README.md +85 -0
- data/Rakefile +32 -0
- data/app/controllers/pay/webhooks/lago_controller.rb +41 -0
- data/app/models/concerns/pay/lago/pay_customer_extensions.rb +15 -0
- data/app/models/concerns/pay/lago/pay_extensions.rb +15 -0
- data/bin/console +15 -0
- data/bin/rails +16 -0
- data/bin/setup +8 -0
- data/config/routes.rb +5 -0
- data/docs/1_customers.md +18 -0
- data/docs/2_charges.md +72 -0
- data/docs/3_subscriptions.md +67 -0
- data/docs/4_webhooks.md +66 -0
- data/lib/pay/lago/attributes.rb +24 -0
- data/lib/pay/lago/billable.rb +148 -0
- data/lib/pay/lago/charge.rb +82 -0
- data/lib/pay/lago/engine.rb +27 -0
- data/lib/pay/lago/error.rb +9 -0
- data/lib/pay/lago/payment_method.rb +37 -0
- data/lib/pay/lago/subscription.rb +134 -0
- data/lib/pay/lago/version.rb +7 -0
- data/lib/pay/lago/webhook_extensions.rb +10 -0
- data/lib/pay/lago/webhooks/customer_payment_provider_created.rb +18 -0
- data/lib/pay/lago/webhooks/invoice_created.rb +15 -0
- data/lib/pay/lago/webhooks/invoice_drafted.rb +11 -0
- data/lib/pay/lago/webhooks/invoice_one_off_created.rb +15 -0
- data/lib/pay/lago/webhooks/invoice_payment_status_updated.rb +14 -0
- data/lib/pay/lago/webhooks/subscription_started.rb +12 -0
- data/lib/pay/lago/webhooks/subscription_terminated.rb +12 -0
- data/lib/pay/lago.rb +91 -0
- data/pay-lago.gemspec +27 -0
- data/sig/pay/lago.rbs +6 -0
- data/test/controllers/pay/webhooks/lago_controller_test.rb +37 -0
- data/test/dummy/.browserslistrc +1 -0
- data/test/dummy/Rakefile +6 -0
- data/test/dummy/app/assets/config/manifest.js +4 -0
- data/test/dummy/app/assets/images/.keep +0 -0
- data/test/dummy/app/assets/stylesheets/application.css +15 -0
- data/test/dummy/app/channels/application_cable/channel.rb +4 -0
- data/test/dummy/app/channels/application_cable/connection.rb +4 -0
- data/test/dummy/app/controllers/application_controller.rb +4 -0
- data/test/dummy/app/controllers/concerns/.keep +0 -0
- data/test/dummy/app/controllers/main_controller.rb +4 -0
- data/test/dummy/app/controllers/payment_methods_controller.rb +5 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/helpers/current_helper.rb +9 -0
- data/test/dummy/app/javascript/application.js +9 -0
- data/test/dummy/app/javascript/controllers/application.js +10 -0
- data/test/dummy/app/javascript/controllers/index.js +11 -0
- data/test/dummy/app/jobs/application_job.rb +2 -0
- data/test/dummy/app/mailers/application_mailer.rb +4 -0
- data/test/dummy/app/models/account.rb +3 -0
- data/test/dummy/app/models/application_record.rb +3 -0
- data/test/dummy/app/models/concerns/.keep +0 -0
- data/test/dummy/app/models/team.rb +10 -0
- data/test/dummy/app/models/user.rb +3 -0
- data/test/dummy/app/views/layouts/application.html.erb +36 -0
- data/test/dummy/app/views/layouts/mailer.html.erb +13 -0
- data/test/dummy/app/views/layouts/mailer.text.erb +1 -0
- data/test/dummy/app/webhooks/charge_succeeded.rb +5 -0
- data/test/dummy/bin/importmap +4 -0
- data/test/dummy/bin/rails +4 -0
- data/test/dummy/bin/rake +4 -0
- data/test/dummy/bin/setup +33 -0
- data/test/dummy/bin/webpack +18 -0
- data/test/dummy/bin/webpack-dev-server +18 -0
- data/test/dummy/config/application.rb +25 -0
- data/test/dummy/config/boot.rb +6 -0
- data/test/dummy/config/cable.yml +9 -0
- data/test/dummy/config/database.yml +3 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +70 -0
- data/test/dummy/config/environments/production.rb +86 -0
- data/test/dummy/config/environments/test.rb +60 -0
- data/test/dummy/config/importmap.rb +8 -0
- data/test/dummy/config/initializers/application_controller_renderer.rb +6 -0
- data/test/dummy/config/initializers/assets.rb +11 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/cookies_serializer.rb +5 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/mime_types.rb +4 -0
- data/test/dummy/config/initializers/pay.rb +11 -0
- data/test/dummy/config/initializers/session_store.rb +3 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +23 -0
- data/test/dummy/config/puma.rb +47 -0
- data/test/dummy/config/routes.rb +4 -0
- data/test/dummy/config/secrets.yml +22 -0
- data/test/dummy/config/spring.rb +6 -0
- data/test/dummy/config/storage.yml +34 -0
- data/test/dummy/config.ru +6 -0
- data/test/dummy/db/migrate/20170205000000_create_users.rb +22 -0
- data/test/dummy/db/schema.rb +125 -0
- data/test/dummy/lib/assets/.keep +0 -0
- data/test/dummy/log/.keep +0 -0
- data/test/dummy/public/404.html +67 -0
- data/test/dummy/public/422.html +67 -0
- data/test/dummy/public/500.html +66 -0
- data/test/dummy/public/apple-touch-icon-precomposed.png +0 -0
- data/test/dummy/public/apple-touch-icon.png +0 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/vendor/javascript/.keep +0 -0
- data/test/fixtures/accounts.yml +2 -0
- data/test/fixtures/pay/customers.yml +7 -0
- data/test/fixtures/teams.yml +4 -0
- data/test/fixtures/users.yml +11 -0
- data/test/pay/lago/billable_test.rb +42 -0
- data/test/pay/lago/charge_test.rb +23 -0
- data/test/pay/lago/subscription_test.rb +48 -0
- data/test/support/fixtures/lago/invoice.one_off_created.json +120 -0
- data/test/support/vcr.rb +28 -0
- data/test/test_helper.rb +66 -0
- data/test/vcr_cassettes/test_add_payment_method_to_lago.yml +56 -0
- data/test/vcr_cassettes/test_can_get_lago_processor_customer.yml +56 -0
- data/test/vcr_cassettes/test_can_make_a_lago_processor_charge.yml +163 -0
- data/test/vcr_cassettes/test_generates_lago_processor_id.yml +107 -0
- data/test/vcr_cassettes/test_lago_cannot_change_quantity.yml +108 -0
- data/test/vcr_cassettes/test_lago_processor_cancel.yml +212 -0
- data/test/vcr_cassettes/test_lago_processor_charge.yml +325 -0
- data/test/vcr_cassettes/test_lago_processor_on_grace_period__is_always_false.yml +108 -0
- data/test/vcr_cassettes/test_lago_processor_pause_raises_an_error.yml +108 -0
- data/test/vcr_cassettes/test_lago_processor_paused__is_always_false.yml +108 -0
- data/test/vcr_cassettes/test_lago_processor_refund_with_premium.yml +378 -0
- data/test/vcr_cassettes/test_lago_processor_refund_without_premium.yml +375 -0
- data/test/vcr_cassettes/test_lago_processor_resume_raises_an_error.yml +108 -0
- data/test/vcr_cassettes/test_lago_processor_subscribe.yml +108 -0
- data/test/vcr_cassettes/test_lago_processor_subscription.yml +160 -0
- data/test/vcr_cassettes/test_lago_processor_swap.yml +212 -0
- metadata +212 -0
@@ -0,0 +1,125 @@
|
|
1
|
+
# This file is auto-generated from the current state of the database. Instead
|
2
|
+
# of editing this file, please use the migrations feature of Active Record to
|
3
|
+
# incrementally modify your database, and then regenerate this schema definition.
|
4
|
+
#
|
5
|
+
# This file is the source Rails uses to define your schema when running `bin/rails
|
6
|
+
# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
|
7
|
+
# be faster and is potentially less error prone than running all of your
|
8
|
+
# migrations from scratch. Old migrations may fail to apply correctly if those
|
9
|
+
# migrations use external dependencies or application code.
|
10
|
+
#
|
11
|
+
# It's strongly recommended that you check this file into your version control system.
|
12
|
+
|
13
|
+
ActiveRecord::Schema[7.1].define(version: 2022_08_31_153001) do
|
14
|
+
create_table "accounts", force: :cascade do |t|
|
15
|
+
t.string "email"
|
16
|
+
t.string "merchant_processor"
|
17
|
+
t.string "pay_data"
|
18
|
+
end
|
19
|
+
|
20
|
+
create_table "pay_charges", force: :cascade do |t|
|
21
|
+
t.integer "customer_id", null: false
|
22
|
+
t.integer "subscription_id"
|
23
|
+
t.string "processor_id", null: false
|
24
|
+
t.integer "amount", null: false
|
25
|
+
t.string "currency"
|
26
|
+
t.integer "application_fee_amount"
|
27
|
+
t.integer "amount_refunded"
|
28
|
+
t.json "metadata"
|
29
|
+
t.json "data"
|
30
|
+
t.datetime "created_at", null: false
|
31
|
+
t.datetime "updated_at", null: false
|
32
|
+
t.index ["customer_id", "processor_id"], name: "index_pay_charges_on_customer_id_and_processor_id", unique: true
|
33
|
+
t.index ["subscription_id"], name: "index_pay_charges_on_subscription_id"
|
34
|
+
end
|
35
|
+
|
36
|
+
create_table "pay_customers", force: :cascade do |t|
|
37
|
+
t.string "owner_type"
|
38
|
+
t.bigint "owner_id"
|
39
|
+
t.string "processor", null: false
|
40
|
+
t.string "processor_id"
|
41
|
+
t.boolean "default"
|
42
|
+
t.json "data"
|
43
|
+
t.datetime "deleted_at"
|
44
|
+
t.datetime "created_at", null: false
|
45
|
+
t.datetime "updated_at", null: false
|
46
|
+
t.index ["owner_type", "owner_id", "deleted_at", "default"], name: "pay_customer_owner_index"
|
47
|
+
t.index ["processor", "processor_id"], name: "index_pay_customers_on_processor_and_processor_id", unique: true
|
48
|
+
end
|
49
|
+
|
50
|
+
create_table "pay_merchants", force: :cascade do |t|
|
51
|
+
t.string "owner_type"
|
52
|
+
t.bigint "owner_id"
|
53
|
+
t.string "processor", null: false
|
54
|
+
t.string "processor_id"
|
55
|
+
t.boolean "default"
|
56
|
+
t.json "data"
|
57
|
+
t.datetime "created_at", null: false
|
58
|
+
t.datetime "updated_at", null: false
|
59
|
+
t.index ["owner_type", "owner_id", "processor"], name: "index_pay_merchants_on_owner_type_and_owner_id_and_processor"
|
60
|
+
end
|
61
|
+
|
62
|
+
create_table "pay_payment_methods", force: :cascade do |t|
|
63
|
+
t.integer "customer_id", null: false
|
64
|
+
t.string "processor_id", null: false
|
65
|
+
t.boolean "default"
|
66
|
+
t.string "type"
|
67
|
+
t.json "data"
|
68
|
+
t.datetime "created_at", null: false
|
69
|
+
t.datetime "updated_at", null: false
|
70
|
+
t.index ["customer_id", "processor_id"], name: "index_pay_payment_methods_on_customer_id_and_processor_id", unique: true
|
71
|
+
end
|
72
|
+
|
73
|
+
create_table "pay_subscriptions", force: :cascade do |t|
|
74
|
+
t.integer "customer_id", null: false
|
75
|
+
t.string "name", null: false
|
76
|
+
t.string "processor_id", null: false
|
77
|
+
t.string "processor_plan", null: false
|
78
|
+
t.integer "quantity", default: 1, null: false
|
79
|
+
t.string "status", null: false
|
80
|
+
t.datetime "current_period_start"
|
81
|
+
t.datetime "current_period_end"
|
82
|
+
t.datetime "trial_ends_at"
|
83
|
+
t.datetime "ends_at"
|
84
|
+
t.decimal "application_fee_percent", precision: 8, scale: 2
|
85
|
+
t.json "metadata"
|
86
|
+
t.json "data"
|
87
|
+
t.datetime "created_at", null: false
|
88
|
+
t.datetime "updated_at", null: false
|
89
|
+
t.boolean "metered"
|
90
|
+
t.string "pause_behavior"
|
91
|
+
t.datetime "pause_starts_at"
|
92
|
+
t.datetime "pause_resumes_at"
|
93
|
+
t.index ["customer_id", "processor_id"], name: "index_pay_subscriptions_on_customer_id_and_processor_id", unique: true
|
94
|
+
t.index ["metered"], name: "index_pay_subscriptions_on_metered"
|
95
|
+
t.index ["pause_starts_at"], name: "index_pay_subscriptions_on_pause_starts_at"
|
96
|
+
end
|
97
|
+
|
98
|
+
create_table "pay_webhooks", force: :cascade do |t|
|
99
|
+
t.string "processor"
|
100
|
+
t.string "event_type"
|
101
|
+
t.json "event"
|
102
|
+
t.datetime "created_at", null: false
|
103
|
+
t.datetime "updated_at", null: false
|
104
|
+
end
|
105
|
+
|
106
|
+
create_table "teams", force: :cascade do |t|
|
107
|
+
t.string "email"
|
108
|
+
t.string "name"
|
109
|
+
t.string "owner_type"
|
110
|
+
t.bigint "owner_id"
|
111
|
+
t.index ["owner_type", "owner_id"], name: "index_teams_on_owner_type_and_owner_id"
|
112
|
+
end
|
113
|
+
|
114
|
+
create_table "users", force: :cascade do |t|
|
115
|
+
t.string "email"
|
116
|
+
t.string "first_name"
|
117
|
+
t.string "last_name"
|
118
|
+
t.text "extra_billing_info"
|
119
|
+
end
|
120
|
+
|
121
|
+
add_foreign_key "pay_charges", "pay_customers", column: "customer_id"
|
122
|
+
add_foreign_key "pay_charges", "pay_subscriptions", column: "subscription_id"
|
123
|
+
add_foreign_key "pay_payment_methods", "pay_customers", column: "customer_id"
|
124
|
+
add_foreign_key "pay_subscriptions", "pay_customers", column: "customer_id"
|
125
|
+
end
|
File without changes
|
File without changes
|
@@ -0,0 +1,67 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>The page you were looking for doesn't exist (404)</title>
|
5
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
6
|
+
<style>
|
7
|
+
body {
|
8
|
+
background-color: #EFEFEF;
|
9
|
+
color: #2E2F30;
|
10
|
+
text-align: center;
|
11
|
+
font-family: arial, sans-serif;
|
12
|
+
margin: 0;
|
13
|
+
}
|
14
|
+
|
15
|
+
div.dialog {
|
16
|
+
width: 95%;
|
17
|
+
max-width: 33em;
|
18
|
+
margin: 4em auto 0;
|
19
|
+
}
|
20
|
+
|
21
|
+
div.dialog > div {
|
22
|
+
border: 1px solid #CCC;
|
23
|
+
border-right-color: #999;
|
24
|
+
border-left-color: #999;
|
25
|
+
border-bottom-color: #BBB;
|
26
|
+
border-top: #B00100 solid 4px;
|
27
|
+
border-top-left-radius: 9px;
|
28
|
+
border-top-right-radius: 9px;
|
29
|
+
background-color: white;
|
30
|
+
padding: 7px 12% 0;
|
31
|
+
box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
|
32
|
+
}
|
33
|
+
|
34
|
+
h1 {
|
35
|
+
font-size: 100%;
|
36
|
+
color: #730E15;
|
37
|
+
line-height: 1.5em;
|
38
|
+
}
|
39
|
+
|
40
|
+
div.dialog > p {
|
41
|
+
margin: 0 0 1em;
|
42
|
+
padding: 1em;
|
43
|
+
background-color: #F7F7F7;
|
44
|
+
border: 1px solid #CCC;
|
45
|
+
border-right-color: #999;
|
46
|
+
border-left-color: #999;
|
47
|
+
border-bottom-color: #999;
|
48
|
+
border-bottom-left-radius: 4px;
|
49
|
+
border-bottom-right-radius: 4px;
|
50
|
+
border-top-color: #DADADA;
|
51
|
+
color: #666;
|
52
|
+
box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
|
53
|
+
}
|
54
|
+
</style>
|
55
|
+
</head>
|
56
|
+
|
57
|
+
<body>
|
58
|
+
<!-- This file lives in public/404.html -->
|
59
|
+
<div class="dialog">
|
60
|
+
<div>
|
61
|
+
<h1>The page you were looking for doesn't exist.</h1>
|
62
|
+
<p>You may have mistyped the address or the page may have moved.</p>
|
63
|
+
</div>
|
64
|
+
<p>If you are the application owner check the logs for more information.</p>
|
65
|
+
</div>
|
66
|
+
</body>
|
67
|
+
</html>
|
@@ -0,0 +1,67 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>The change you wanted was rejected (422)</title>
|
5
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
6
|
+
<style>
|
7
|
+
body {
|
8
|
+
background-color: #EFEFEF;
|
9
|
+
color: #2E2F30;
|
10
|
+
text-align: center;
|
11
|
+
font-family: arial, sans-serif;
|
12
|
+
margin: 0;
|
13
|
+
}
|
14
|
+
|
15
|
+
div.dialog {
|
16
|
+
width: 95%;
|
17
|
+
max-width: 33em;
|
18
|
+
margin: 4em auto 0;
|
19
|
+
}
|
20
|
+
|
21
|
+
div.dialog > div {
|
22
|
+
border: 1px solid #CCC;
|
23
|
+
border-right-color: #999;
|
24
|
+
border-left-color: #999;
|
25
|
+
border-bottom-color: #BBB;
|
26
|
+
border-top: #B00100 solid 4px;
|
27
|
+
border-top-left-radius: 9px;
|
28
|
+
border-top-right-radius: 9px;
|
29
|
+
background-color: white;
|
30
|
+
padding: 7px 12% 0;
|
31
|
+
box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
|
32
|
+
}
|
33
|
+
|
34
|
+
h1 {
|
35
|
+
font-size: 100%;
|
36
|
+
color: #730E15;
|
37
|
+
line-height: 1.5em;
|
38
|
+
}
|
39
|
+
|
40
|
+
div.dialog > p {
|
41
|
+
margin: 0 0 1em;
|
42
|
+
padding: 1em;
|
43
|
+
background-color: #F7F7F7;
|
44
|
+
border: 1px solid #CCC;
|
45
|
+
border-right-color: #999;
|
46
|
+
border-left-color: #999;
|
47
|
+
border-bottom-color: #999;
|
48
|
+
border-bottom-left-radius: 4px;
|
49
|
+
border-bottom-right-radius: 4px;
|
50
|
+
border-top-color: #DADADA;
|
51
|
+
color: #666;
|
52
|
+
box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
|
53
|
+
}
|
54
|
+
</style>
|
55
|
+
</head>
|
56
|
+
|
57
|
+
<body>
|
58
|
+
<!-- This file lives in public/422.html -->
|
59
|
+
<div class="dialog">
|
60
|
+
<div>
|
61
|
+
<h1>The change you wanted was rejected.</h1>
|
62
|
+
<p>Maybe you tried to change something you didn't have access to.</p>
|
63
|
+
</div>
|
64
|
+
<p>If you are the application owner check the logs for more information.</p>
|
65
|
+
</div>
|
66
|
+
</body>
|
67
|
+
</html>
|
@@ -0,0 +1,66 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>We're sorry, but something went wrong (500)</title>
|
5
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
6
|
+
<style>
|
7
|
+
body {
|
8
|
+
background-color: #EFEFEF;
|
9
|
+
color: #2E2F30;
|
10
|
+
text-align: center;
|
11
|
+
font-family: arial, sans-serif;
|
12
|
+
margin: 0;
|
13
|
+
}
|
14
|
+
|
15
|
+
div.dialog {
|
16
|
+
width: 95%;
|
17
|
+
max-width: 33em;
|
18
|
+
margin: 4em auto 0;
|
19
|
+
}
|
20
|
+
|
21
|
+
div.dialog > div {
|
22
|
+
border: 1px solid #CCC;
|
23
|
+
border-right-color: #999;
|
24
|
+
border-left-color: #999;
|
25
|
+
border-bottom-color: #BBB;
|
26
|
+
border-top: #B00100 solid 4px;
|
27
|
+
border-top-left-radius: 9px;
|
28
|
+
border-top-right-radius: 9px;
|
29
|
+
background-color: white;
|
30
|
+
padding: 7px 12% 0;
|
31
|
+
box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
|
32
|
+
}
|
33
|
+
|
34
|
+
h1 {
|
35
|
+
font-size: 100%;
|
36
|
+
color: #730E15;
|
37
|
+
line-height: 1.5em;
|
38
|
+
}
|
39
|
+
|
40
|
+
div.dialog > p {
|
41
|
+
margin: 0 0 1em;
|
42
|
+
padding: 1em;
|
43
|
+
background-color: #F7F7F7;
|
44
|
+
border: 1px solid #CCC;
|
45
|
+
border-right-color: #999;
|
46
|
+
border-left-color: #999;
|
47
|
+
border-bottom-color: #999;
|
48
|
+
border-bottom-left-radius: 4px;
|
49
|
+
border-bottom-right-radius: 4px;
|
50
|
+
border-top-color: #DADADA;
|
51
|
+
color: #666;
|
52
|
+
box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
|
53
|
+
}
|
54
|
+
</style>
|
55
|
+
</head>
|
56
|
+
|
57
|
+
<body>
|
58
|
+
<!-- This file lives in public/500.html -->
|
59
|
+
<div class="dialog">
|
60
|
+
<div>
|
61
|
+
<h1>We're sorry, but something went wrong.</h1>
|
62
|
+
</div>
|
63
|
+
<p>If you are the application owner check the logs for more information.</p>
|
64
|
+
</div>
|
65
|
+
</body>
|
66
|
+
</html>
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class Pay::Lago::Billable::Test < ActiveSupport::TestCase
|
4
|
+
setup do
|
5
|
+
@pay_customer = pay_customers(:lago)
|
6
|
+
@pay_customer.processor_id = @pay_customer.pay_external_id
|
7
|
+
end
|
8
|
+
|
9
|
+
test "can get lago processor customer" do
|
10
|
+
assert_equal @pay_customer.customer.external_id, @pay_customer.pay_external_id
|
11
|
+
end
|
12
|
+
|
13
|
+
test "can make a lago processor charge" do
|
14
|
+
assert_difference "Pay::Charge.count" do
|
15
|
+
@pay_customer.charge(10_00)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Subscribe to a plan with the code "default" in Lago
|
20
|
+
test "lago processor subscribe" do
|
21
|
+
assert_difference "Pay::Subscription.count" do
|
22
|
+
@pay_customer.subscribe
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
test "generates lago processor_id" do
|
27
|
+
user = users(:none)
|
28
|
+
pay_customer = user.set_payment_processor :lago
|
29
|
+
assert_nil pay_customer.processor_id
|
30
|
+
pay_customer.customer
|
31
|
+
assert_not_nil pay_customer.processor_id
|
32
|
+
end
|
33
|
+
|
34
|
+
test "add payment method to lago" do
|
35
|
+
assert_difference "Pay::PaymentMethod.count" do
|
36
|
+
@pay_customer.add_payment_method("x", default: true)
|
37
|
+
end
|
38
|
+
|
39
|
+
payment_method = @pay_customer.default_payment_method
|
40
|
+
assert_equal "card", payment_method.type
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class Pay::Lago::Charge::Test < ActiveSupport::TestCase
|
4
|
+
setup do
|
5
|
+
@pay_customer = pay_customers(:lago)
|
6
|
+
@charge = @pay_customer.charge(10_00)
|
7
|
+
@charge.payment_processor.update_charge!(payment_status: "succeeded")
|
8
|
+
end
|
9
|
+
|
10
|
+
test "lago processor charge" do
|
11
|
+
assert_equal @charge.processor_id, @charge.payment_processor.charge.lago_id
|
12
|
+
end
|
13
|
+
|
14
|
+
test "lago processor refund with premium" do
|
15
|
+
assert_nil @charge.amount_refunded
|
16
|
+
@charge.refund!(5_00)
|
17
|
+
assert_equal 5_00, @charge.reload.amount_refunded
|
18
|
+
end
|
19
|
+
|
20
|
+
test "lago processor refund without premium" do
|
21
|
+
assert_raises(Pay::Lago::Error, "Creating a credit note requires Lago Premium.") { @charge.refund!(5_00) }
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class Pay::Lago::Subscription::Test < ActiveSupport::TestCase
|
4
|
+
setup do
|
5
|
+
@pay_customer = pay_customers(:lago)
|
6
|
+
@subscription = @pay_customer.subscribe
|
7
|
+
end
|
8
|
+
|
9
|
+
test "lago processor subscription" do
|
10
|
+
assert_equal @subscription.processor_id, @subscription.processor_subscription.external_id
|
11
|
+
end
|
12
|
+
|
13
|
+
test "lago processor cancel" do
|
14
|
+
freeze_time do
|
15
|
+
@subscription.cancel
|
16
|
+
@subscription.reload
|
17
|
+
assert_equal @subscription.cancelled?, true
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
test "lago processor on_grace_period? is always false" do
|
22
|
+
assert_equal @subscription.on_grace_period?, false
|
23
|
+
end
|
24
|
+
|
25
|
+
test "lago processor paused? is always false" do
|
26
|
+
assert_equal @subscription.paused?, false
|
27
|
+
end
|
28
|
+
|
29
|
+
test "lago processor resume raises an error" do
|
30
|
+
assert_raises(Pay::Lago::Error, "Lago subscriptions cannot be paused.") { @subscription.resume }
|
31
|
+
end
|
32
|
+
|
33
|
+
test "lago processor pause raises an error" do
|
34
|
+
assert_raises(Pay::Lago::Error, "Lago subscriptions cannot be paused.") { @subscription.pause }
|
35
|
+
end
|
36
|
+
|
37
|
+
test "lago processor swap" do
|
38
|
+
travel_to Date.parse("2023-09-18") do
|
39
|
+
@subscription.swap("another_plan")
|
40
|
+
assert_equal @subscription.changing_plan?, true
|
41
|
+
assert_equal @subscription.subscription.next_plan_code, "another_plan"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
test "lago cannot change quantity" do
|
46
|
+
assert_raises(Pay::Lago::Error, "Lago subscriptions do not implement quantity.") { @subscription.change_quantity(3) }
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
{
|
2
|
+
"webhook_type": "invoice.one_off_created",
|
3
|
+
"object_type": "invoice",
|
4
|
+
"invoice": {
|
5
|
+
"lago_id": "b7b6a31f-b81b-4ae5-a9c8-05c407e47416",
|
6
|
+
"sequential_id": 4,
|
7
|
+
"number": "ONE-0499-001-004",
|
8
|
+
"issuing_date": "2023-05-05",
|
9
|
+
"invoice_type": "one_off",
|
10
|
+
"status": "finalized",
|
11
|
+
"payment_status": "pending",
|
12
|
+
"currency": "USD",
|
13
|
+
"fees_amount_cents": 25000,
|
14
|
+
"vat_amount_cents": 2500,
|
15
|
+
"coupons_amount_cents": 0,
|
16
|
+
"credit_notes_amount_cents": 0,
|
17
|
+
"sub_total_vat_excluded_amount_cents": 25000,
|
18
|
+
"sub_total_vat_included_amount_cents": 27500,
|
19
|
+
"total_amount_cents": 27500,
|
20
|
+
"prepaid_credit_amount_cents": 0,
|
21
|
+
"file_url": null,
|
22
|
+
"version_number": 3,
|
23
|
+
"legacy": false,
|
24
|
+
"amount_currency": "USD",
|
25
|
+
"vat_amount_currency": "USD",
|
26
|
+
"credit_amount_currency": "USD",
|
27
|
+
"total_amount_currency": "USD",
|
28
|
+
"amount_cents": 25000,
|
29
|
+
"credit_amount_cents": 0,
|
30
|
+
"customer": {
|
31
|
+
"lago_id": "37cee916-5751-4665-9774-aa05bb1a2ffd",
|
32
|
+
"external_id": "5eb02857-a71e-4ea2-bcf9-57d3a41bc6ba",
|
33
|
+
"name": "Gavin Belson",
|
34
|
+
"sequential_id": 1,
|
35
|
+
"slug": "TWI-2B86-001",
|
36
|
+
"created_at": "2022-06-01T09:01:12Z",
|
37
|
+
"country": "US",
|
38
|
+
"address_line1": "5230 Penfield Ave",
|
39
|
+
"address_line2": null,
|
40
|
+
"state": "CA",
|
41
|
+
"zipcode": "91364",
|
42
|
+
"email": "dinesh@piedpiper.test",
|
43
|
+
"city": "Woodland Hills",
|
44
|
+
"url": "http://hooli.com",
|
45
|
+
"phone": "1-171-883-3711 x245",
|
46
|
+
"logo_url": "http://hooli.com/logo.png",
|
47
|
+
"legal_name": "Coleman-Blair",
|
48
|
+
"legal_number": "49-008-2965",
|
49
|
+
"currency": "USD",
|
50
|
+
"timezone": null,
|
51
|
+
"applicable_timezone": "UTC",
|
52
|
+
"billing_configuration": {
|
53
|
+
"invoice_grace_period": null,
|
54
|
+
"payment_provider": null,
|
55
|
+
"vat_rate": 0,
|
56
|
+
"document_locale": null
|
57
|
+
}
|
58
|
+
},
|
59
|
+
"fees": [
|
60
|
+
{
|
61
|
+
"lago_id": "5fe06606-25c8-4aa8-bce8-a19a12383d51",
|
62
|
+
"lago_group_id": null,
|
63
|
+
"lago_invoice_id": "b7b6a31f-b81b-4ae5-a9c8-05c407e47416",
|
64
|
+
"lago_true_up_fee_id": null,
|
65
|
+
"lago_true_up_parent_fee_id": null,
|
66
|
+
"item": {
|
67
|
+
"type": "add_on",
|
68
|
+
"code": "setup_fee",
|
69
|
+
"name": "Setup Fee",
|
70
|
+
"lago_item_id": "b3f1967d-bf84-44a7-b3e9-207024a6aaa5",
|
71
|
+
"item_type": "AddOn"
|
72
|
+
},
|
73
|
+
"amount_cents": 5000,
|
74
|
+
"amount_currency": "USD",
|
75
|
+
"vat_amount_cents": 500,
|
76
|
+
"vat_amount_currency": "USD",
|
77
|
+
"total_amount_cents": 5500,
|
78
|
+
"total_amount_currency": "USD",
|
79
|
+
"units": "5.0",
|
80
|
+
"description": "",
|
81
|
+
"unit_amount_cents": 1000,
|
82
|
+
"events_count": null,
|
83
|
+
"payment_status": "pending",
|
84
|
+
"created_at": "2023-05-05T15:05:27Z",
|
85
|
+
"succeeded_at": null,
|
86
|
+
"failed_at": null,
|
87
|
+
"refunded_at": null
|
88
|
+
},
|
89
|
+
{
|
90
|
+
"lago_id": "a74705a1-05f2-41cb-b315-ab0c858c215d",
|
91
|
+
"lago_group_id": null,
|
92
|
+
"lago_invoice_id": "b7b6a31f-b81b-4ae5-a9c8-05c407e47416",
|
93
|
+
"lago_true_up_fee_id": null,
|
94
|
+
"lago_true_up_parent_fee_id": null,
|
95
|
+
"item": {
|
96
|
+
"type": "add_on",
|
97
|
+
"code": "customer_success_fee",
|
98
|
+
"name": "Customer Success",
|
99
|
+
"lago_item_id": "66b75fbf-3410-4251-9105-b4b3d5922a1d",
|
100
|
+
"item_type": "AddOn"
|
101
|
+
},
|
102
|
+
"amount_cents": 20000,
|
103
|
+
"amount_currency": "USD",
|
104
|
+
"vat_amount_cents": 2000,
|
105
|
+
"vat_amount_currency": "USD",
|
106
|
+
"total_amount_cents": 22000,
|
107
|
+
"total_amount_currency": "USD",
|
108
|
+
"units": "1.0",
|
109
|
+
"description": "My new description",
|
110
|
+
"unit_amount_cents": 20000,
|
111
|
+
"events_count": null,
|
112
|
+
"payment_status": "pending",
|
113
|
+
"created_at": "2023-05-05T15:05:27Z",
|
114
|
+
"succeeded_at": null,
|
115
|
+
"failed_at": null,
|
116
|
+
"refunded_at": null
|
117
|
+
}
|
118
|
+
]
|
119
|
+
}
|
120
|
+
}
|
data/test/support/vcr.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require "vcr"
|
2
|
+
|
3
|
+
unless ENV["SKIP_VCR"]
|
4
|
+
require "webmock/minitest"
|
5
|
+
|
6
|
+
VCR.configure do |c|
|
7
|
+
c.cassette_library_dir = "test/vcr_cassettes"
|
8
|
+
c.hook_into :webmock
|
9
|
+
c.allow_http_connections_when_no_cassette = true
|
10
|
+
c.filter_sensitive_data("<LAGO_API_KEY>") { Pay::Lago.api_key }
|
11
|
+
end
|
12
|
+
|
13
|
+
class ActiveSupport::TestCase
|
14
|
+
setup do
|
15
|
+
# Test filenames are case sensitive in CI
|
16
|
+
VCR.insert_cassette name, allow_unused_http_interactions: false, record_on_error: false
|
17
|
+
end
|
18
|
+
|
19
|
+
teardown do
|
20
|
+
cassette = VCR.current_cassette
|
21
|
+
VCR.eject_cassette
|
22
|
+
rescue VCR::Errors::UnusedHTTPInteractionError
|
23
|
+
puts
|
24
|
+
puts "Unused HTTP requests in cassette: #{cassette.file}"
|
25
|
+
raise
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|