mirror of
https://github.com/zulip/zulip.git
synced 2025-10-26 09:34:02 +00:00
Compare commits
103 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2bb3af1ade | ||
|
|
5797f013b3 | ||
|
|
1195841dfb | ||
|
|
dca727f178 | ||
|
|
77d5a37539 | ||
|
|
2c16b9905d | ||
|
|
3c5e69aa0d | ||
|
|
32af9a95d5 | ||
|
|
3eeaafed73 | ||
|
|
e9e25fd319 | ||
|
|
a1f62a66ae | ||
|
|
d52a5a1d2d | ||
|
|
f2022834bd | ||
|
|
54f981bfeb | ||
|
|
d6daf11e03 | ||
|
|
4e5bb844b4 | ||
|
|
9230d3304f | ||
|
|
cd07bcdd29 | ||
|
|
89cbe7f53b | ||
|
|
a41cbad45c | ||
|
|
633c91facf | ||
|
|
e8acffcb8a | ||
|
|
342c7d946a | ||
|
|
f95c807100 | ||
|
|
d11f32909c | ||
|
|
d232ce0078 | ||
|
|
4f7c463257 | ||
|
|
2eb713552f | ||
|
|
2da9d25a9a | ||
|
|
388565c7bd | ||
|
|
a88643313f | ||
|
|
6d7a3cf4d7 | ||
|
|
8f1ac265e4 | ||
|
|
38af195ab4 | ||
|
|
e63245eee3 | ||
|
|
cb86bb7d84 | ||
|
|
b1d5bb4ae4 | ||
|
|
1c0653136c | ||
|
|
3db5a03f5c | ||
|
|
967ff6f770 | ||
|
|
67cb813740 | ||
|
|
5f2c3cd835 | ||
|
|
7b897cac77 | ||
|
|
94395c6dd8 | ||
|
|
38cd21b1b4 | ||
|
|
e074165c1b | ||
|
|
abe645f1d3 | ||
|
|
064f5ccf8f | ||
|
|
19fbf9c3cf | ||
|
|
eb7e44f7ed | ||
|
|
b08824d92f | ||
|
|
d9a1617d00 | ||
|
|
6f2903dd29 | ||
|
|
afd06bdb46 | ||
|
|
57cdef11c4 | ||
|
|
8020247b79 | ||
|
|
01adbc3a2e | ||
|
|
8b739e4d0b | ||
|
|
1fb079d63b | ||
|
|
95482235ad | ||
|
|
f8d74fab08 | ||
|
|
049b83f0bb | ||
|
|
bffa709ec8 | ||
|
|
ddae999601 | ||
|
|
57cd185366 | ||
|
|
d39a7ea429 | ||
|
|
088f8745d1 | ||
|
|
f32f02da8b | ||
|
|
76d6d69568 | ||
|
|
4a1e98f574 | ||
|
|
487632b454 | ||
|
|
848276ee3b | ||
|
|
d740b1ae19 | ||
|
|
48d8b90863 | ||
|
|
eeeb947187 | ||
|
|
507cf1d322 | ||
|
|
f3f90bb527 | ||
|
|
46d6541958 | ||
|
|
13eaa49a42 | ||
|
|
1157aef8b3 | ||
|
|
65eb125d61 | ||
|
|
713d6739ec | ||
|
|
70c0c7a83f | ||
|
|
c1ee7692d6 | ||
|
|
ad336800d0 | ||
|
|
e9e3eafdde | ||
|
|
df68a3e963 | ||
|
|
faaf84bb01 | ||
|
|
c082547021 | ||
|
|
d6c7199ce1 | ||
|
|
29b3dd0852 | ||
|
|
0ffc42083e | ||
|
|
019e5a17f0 | ||
|
|
177673c84e | ||
|
|
f6c1a31988 | ||
|
|
870cd00f5f | ||
|
|
7db599deaa | ||
|
|
84d2be5e0c | ||
|
|
d360833d7f | ||
|
|
bc3db1701b | ||
|
|
e8aca7b723 | ||
|
|
7a72390710 | ||
|
|
3ffe4ca3e5 |
@@ -1,6 +0,0 @@
|
|||||||
> 0.2%
|
|
||||||
> 0.2% in US
|
|
||||||
last 2 versions
|
|
||||||
Firefox ESR
|
|
||||||
not dead
|
|
||||||
Chrome 26 # similar to PhantomJS
|
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
# See CircleCI upstream's docs on this config format:
|
# See CircleCI upstream's docs on this config format:
|
||||||
# https://circleci.com/docs/2.0/language-python/
|
# https://circleci.com/docs/2.0/language-python/
|
||||||
#
|
#
|
||||||
version: 2.0
|
version: 2
|
||||||
aliases:
|
aliases:
|
||||||
- &create_cache_directories
|
- &create_cache_directories
|
||||||
run:
|
run:
|
||||||
@@ -82,11 +82,10 @@ aliases:
|
|||||||
|| echo "Error in uploading coverage reports to codecov.io."
|
|| echo "Error in uploading coverage reports to codecov.io."
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
"xenial-backend-frontend-python3.5":
|
"trusty-python-3.4":
|
||||||
docker:
|
docker:
|
||||||
# This is built from tools/circleci/images/xenial/Dockerfile .
|
# This is built from tools/circleci/images/trusty/Dockerfile .
|
||||||
# Xenial ships with Python 3.5.
|
- image: gregprice/circleci:trusty-python-5.test
|
||||||
- image: gregprice/circleci:xenial-python-4.test
|
|
||||||
|
|
||||||
working_directory: ~/zulip
|
working_directory: ~/zulip
|
||||||
|
|
||||||
@@ -101,25 +100,34 @@ jobs:
|
|||||||
- *save_cache_requirements
|
- *save_cache_requirements
|
||||||
- *run_backend_tests
|
- *run_backend_tests
|
||||||
- *run_frontend_tests
|
- *run_frontend_tests
|
||||||
# We only need to upload coverage reports on whichever platform
|
|
||||||
# runs the frontend tests.
|
|
||||||
- *upload_coverage_report
|
- *upload_coverage_report
|
||||||
|
|
||||||
- store_artifacts:
|
# - store_artifacts: # TODO
|
||||||
path: ./var/casper/
|
# path: var/casper/
|
||||||
destination: casper
|
# # also /tmp/zulip-test-event-log/
|
||||||
|
# destination: test-reports
|
||||||
|
|
||||||
- store_artifacts:
|
"xenial-python-3.5":
|
||||||
path: ../../../tmp/zulip-test-event-log/
|
docker:
|
||||||
destination: test-reports
|
# This is built from tools/circleci/images/xenial/Dockerfile .
|
||||||
|
- image: gregprice/circleci:xenial-python-4.test
|
||||||
|
|
||||||
- store_test_results:
|
working_directory: ~/zulip
|
||||||
path: ./var/xunit-test-results/casper/
|
|
||||||
|
|
||||||
"bionic-backend-python3.6":
|
steps:
|
||||||
|
- checkout
|
||||||
|
- *create_cache_directories
|
||||||
|
- *restore_cache_package_json
|
||||||
|
- *restore_cache_requirements
|
||||||
|
- *install_dependencies
|
||||||
|
- *save_cache_package_json
|
||||||
|
- *save_cache_requirements
|
||||||
|
- *run_backend_tests
|
||||||
|
- *upload_coverage_report
|
||||||
|
|
||||||
|
"bionic-python-3.6":
|
||||||
docker:
|
docker:
|
||||||
# This is built from tools/circleci/images/bionic/Dockerfile .
|
# This is built from tools/circleci/images/bionic/Dockerfile .
|
||||||
# Bionic ships with Python 3.6.
|
|
||||||
- image: gregprice/circleci:bionic-python-1.test
|
- image: gregprice/circleci:bionic-python-1.test
|
||||||
|
|
||||||
working_directory: ~/zulip
|
working_directory: ~/zulip
|
||||||
@@ -134,7 +142,7 @@ jobs:
|
|||||||
command: |
|
command: |
|
||||||
# Temporary hack till `sudo service redis-server start` gets fixes in Bionic. See
|
# Temporary hack till `sudo service redis-server start` gets fixes in Bionic. See
|
||||||
# https://chat.zulip.org/#narrow/stream/3-backend/topic/Ubuntu.20bionic.20CircleCI
|
# https://chat.zulip.org/#narrow/stream/3-backend/topic/Ubuntu.20bionic.20CircleCI
|
||||||
sudo sed -i '/^bind/s/bind.*/bind 0.0.0.0/' /etc/redis/redis.conf
|
redis-server --daemonize yes
|
||||||
|
|
||||||
- *restore_cache_package_json
|
- *restore_cache_package_json
|
||||||
- *restore_cache_requirements
|
- *restore_cache_requirements
|
||||||
@@ -142,10 +150,12 @@ jobs:
|
|||||||
- *save_cache_package_json
|
- *save_cache_package_json
|
||||||
- *save_cache_requirements
|
- *save_cache_requirements
|
||||||
- *run_backend_tests
|
- *run_backend_tests
|
||||||
|
- *upload_coverage_report
|
||||||
|
|
||||||
workflows:
|
workflows:
|
||||||
version: 2
|
version: 2
|
||||||
build:
|
build:
|
||||||
jobs:
|
jobs:
|
||||||
- "xenial-backend-frontend-python3.5"
|
- "trusty-python-3.4"
|
||||||
- "bionic-backend-python3.6"
|
- "xenial-python-3.5"
|
||||||
|
- "bionic-python-3.6"
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ coverage:
|
|||||||
project:
|
project:
|
||||||
default:
|
default:
|
||||||
target: auto
|
target: auto
|
||||||
# Codecov has the tendency to report a lot of false negatives,
|
threshold: 0.50
|
||||||
# so we basically suppress comments completely.
|
|
||||||
threshold: 50%
|
|
||||||
base: auto
|
base: auto
|
||||||
patch: off
|
patch: off
|
||||||
|
|||||||
@@ -6,20 +6,20 @@ charset = utf-8
|
|||||||
trim_trailing_whitespace = true
|
trim_trailing_whitespace = true
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
|
|
||||||
[*.{sh,py,pyi,js,ts,json,yml,xml,css,md,markdown,handlebars,html}]
|
[*.{sh,py,pyi,js,json,yml,xml,css,md,markdown,handlebars,html}]
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 4
|
indent_size = 4
|
||||||
|
|
||||||
[*.py]
|
[*.{py}]
|
||||||
max_line_length = 110
|
max_line_length = 110
|
||||||
|
|
||||||
[*.{js,ts}]
|
[*.{js}]
|
||||||
max_line_length = 100
|
max_line_length = 120
|
||||||
|
|
||||||
[*.{svg,rb,pp,pl}]
|
[*.{svg,rb,pp,pl}]
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
|
|
||||||
[*.cfg]
|
[*.{cfg}]
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 8
|
indent_size = 8
|
||||||
|
|||||||
@@ -1,10 +1,2 @@
|
|||||||
# This is intended for generated files and vendored third-party files.
|
static/js/blueslip.js
|
||||||
# For our source code, instead of adding files here, consider using
|
static/webpack-bundles
|
||||||
# specific eslint-disable comments in the files themselves.
|
|
||||||
|
|
||||||
/docs/_build
|
|
||||||
/static/generated
|
|
||||||
/static/third
|
|
||||||
/static/webpack-bundles
|
|
||||||
/var
|
|
||||||
/zulip-py3-venv
|
|
||||||
|
|||||||
255
.eslintrc.json
255
.eslintrc.json
@@ -4,8 +4,6 @@
|
|||||||
"es6": true
|
"es6": true
|
||||||
},
|
},
|
||||||
"parserOptions": {
|
"parserOptions": {
|
||||||
"ecmaVersion": 2019,
|
|
||||||
"warnOnUnsupportedTypeScriptVersion": false,
|
|
||||||
"sourceType": "module"
|
"sourceType": "module"
|
||||||
},
|
},
|
||||||
"globals": {
|
"globals": {
|
||||||
@@ -18,6 +16,7 @@
|
|||||||
"LightboxCanvas": false,
|
"LightboxCanvas": false,
|
||||||
"MessageListData": false,
|
"MessageListData": false,
|
||||||
"MessageListView": false,
|
"MessageListView": false,
|
||||||
|
"PerfectScrollbar": false,
|
||||||
"Plotly": false,
|
"Plotly": false,
|
||||||
"SockJS": false,
|
"SockJS": false,
|
||||||
"Socket": false,
|
"Socket": false,
|
||||||
@@ -70,7 +69,6 @@
|
|||||||
"hash_util": false,
|
"hash_util": false,
|
||||||
"hashchange": false,
|
"hashchange": false,
|
||||||
"helpers": false,
|
"helpers": false,
|
||||||
"history": false,
|
|
||||||
"home_msg_list": false,
|
"home_msg_list": false,
|
||||||
"hotspots": false,
|
"hotspots": false,
|
||||||
"i18n": false,
|
"i18n": false,
|
||||||
@@ -88,7 +86,6 @@
|
|||||||
"localStorage": false,
|
"localStorage": false,
|
||||||
"local_message": false,
|
"local_message": false,
|
||||||
"localstorage": false,
|
"localstorage": false,
|
||||||
"location": false,
|
|
||||||
"markdown": false,
|
"markdown": false,
|
||||||
"marked": false,
|
"marked": false,
|
||||||
"md5": false,
|
"md5": false,
|
||||||
@@ -149,7 +146,6 @@
|
|||||||
"settings_bots": false,
|
"settings_bots": false,
|
||||||
"settings_display": false,
|
"settings_display": false,
|
||||||
"settings_emoji": false,
|
"settings_emoji": false,
|
||||||
"settings_exports": false,
|
|
||||||
"settings_linkifiers": false,
|
"settings_linkifiers": false,
|
||||||
"settings_invites": false,
|
"settings_invites": false,
|
||||||
"settings_muting": false,
|
"settings_muting": false,
|
||||||
@@ -173,7 +169,6 @@
|
|||||||
"stream_muting": false,
|
"stream_muting": false,
|
||||||
"stream_popover": false,
|
"stream_popover": false,
|
||||||
"stream_sort": false,
|
"stream_sort": false,
|
||||||
"stream_ui_updates": false,
|
|
||||||
"StripeCheckout": false,
|
"StripeCheckout": false,
|
||||||
"submessage": false,
|
"submessage": false,
|
||||||
"subs": false,
|
"subs": false,
|
||||||
@@ -181,6 +176,7 @@
|
|||||||
"templates": false,
|
"templates": false,
|
||||||
"tictactoe_widget": false,
|
"tictactoe_widget": false,
|
||||||
"timerender": false,
|
"timerender": false,
|
||||||
|
"toMarkdown": false,
|
||||||
"todo_widget": false,
|
"todo_widget": false,
|
||||||
"top_left_corner": false,
|
"top_left_corner": false,
|
||||||
"topic_data": false,
|
"topic_data": false,
|
||||||
@@ -193,6 +189,7 @@
|
|||||||
"typing": false,
|
"typing": false,
|
||||||
"typing_data": false,
|
"typing_data": false,
|
||||||
"typing_events": false,
|
"typing_events": false,
|
||||||
|
"typing_status": false,
|
||||||
"ui": false,
|
"ui": false,
|
||||||
"ui_init": false,
|
"ui_init": false,
|
||||||
"ui_report": false,
|
"ui_report": false,
|
||||||
@@ -223,9 +220,9 @@
|
|||||||
"array-callback-return": "error",
|
"array-callback-return": "error",
|
||||||
"array-bracket-spacing": "error",
|
"array-bracket-spacing": "error",
|
||||||
"arrow-spacing": [ "error", { "before": true, "after": true } ],
|
"arrow-spacing": [ "error", { "before": true, "after": true } ],
|
||||||
"block-scoped-var": "error",
|
"block-scoped-var": 2,
|
||||||
"brace-style": [ "error", "1tbs", { "allowSingleLine": true } ],
|
"brace-style": [ "error", "1tbs", { "allowSingleLine": true } ],
|
||||||
"camelcase": "off",
|
"camelcase": 0,
|
||||||
"comma-dangle": [ "error",
|
"comma-dangle": [ "error",
|
||||||
{
|
{
|
||||||
"arrays": "always-multiline",
|
"arrays": "always-multiline",
|
||||||
@@ -241,16 +238,17 @@
|
|||||||
"after": true
|
"after": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"complexity": [ "off", 4 ],
|
"complexity": [ 0, 4 ],
|
||||||
"curly": "error",
|
"curly": 2,
|
||||||
"dot-notation": [ "error", { "allowKeywords": true } ],
|
"dot-notation": [ "error", { "allowKeywords": true } ],
|
||||||
"empty-returns/main": "error",
|
"empty-returns/main": "error",
|
||||||
"eol-last": [ "error", "always" ],
|
"eol-last": [ "error", "always" ],
|
||||||
"eqeqeq": "error",
|
"eqeqeq": 2,
|
||||||
"func-style": [ "off", "expression" ],
|
"func-style": [ "off", "expression" ],
|
||||||
"guard-for-in": "error",
|
"guard-for-in": 2,
|
||||||
"indent": ["error", 4, {
|
"indent": ["error", 4, {
|
||||||
"ArrayExpression": "first",
|
"ArrayExpression": "first",
|
||||||
|
"outerIIFEBody": 0,
|
||||||
"ObjectExpression": "first",
|
"ObjectExpression": "first",
|
||||||
"SwitchCase": 0,
|
"SwitchCase": 0,
|
||||||
"CallExpression": {"arguments": "first"},
|
"CallExpression": {"arguments": "first"},
|
||||||
@@ -274,7 +272,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"max-depth": [ "off", 4 ],
|
"max-depth": [ 0, 4 ],
|
||||||
"max-len": [ "error", 100, 2,
|
"max-len": [ "error", 100, 2,
|
||||||
{
|
{
|
||||||
"ignoreUrls": true,
|
"ignoreUrls": true,
|
||||||
@@ -284,76 +282,76 @@
|
|||||||
"ignoreTemplateLiterals": true
|
"ignoreTemplateLiterals": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"max-params": [ "off", 3 ],
|
"max-params": [ 0, 3 ],
|
||||||
"max-statements": [ "off", 10 ],
|
"max-statements": [ 0, 10 ],
|
||||||
"new-cap": [ "error",
|
"new-cap": [ "error",
|
||||||
{
|
{
|
||||||
"newIsCap": true,
|
"newIsCap": true,
|
||||||
"capIsNew": false
|
"capIsNew": false
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"new-parens": "error",
|
"new-parens": 2,
|
||||||
"newline-per-chained-call": "off",
|
"newline-per-chained-call": 0,
|
||||||
"no-alert": "error",
|
"no-alert": 2,
|
||||||
"no-array-constructor": "error",
|
"no-array-constructor": "error",
|
||||||
"no-bitwise": "error",
|
"no-bitwise": 2,
|
||||||
"no-caller": "error",
|
"no-caller": 2,
|
||||||
"no-case-declarations": "error",
|
"no-case-declarations": "error",
|
||||||
"no-catch-shadow": "error",
|
"no-catch-shadow": 2,
|
||||||
"no-console": "off",
|
"no-console": 0,
|
||||||
"no-const-assign": "error",
|
"no-const-assign": "error",
|
||||||
"no-control-regex": "error",
|
"no-control-regex": 2,
|
||||||
"no-debugger": "error",
|
"no-debugger": 2,
|
||||||
"no-delete-var": "error",
|
"no-delete-var": 2,
|
||||||
"no-div-regex": "error",
|
"no-div-regex": 2,
|
||||||
"no-dupe-class-members": "error",
|
"no-dupe-class-members": "error",
|
||||||
"no-dupe-keys": "error",
|
"no-dupe-keys": 2,
|
||||||
"no-duplicate-imports": "error",
|
"no-duplicate-imports": "error",
|
||||||
"no-else-return": "error",
|
"no-else-return": 2,
|
||||||
"no-empty": "error",
|
"no-empty": 2,
|
||||||
"no-empty-character-class": "error",
|
"no-empty-character-class": 2,
|
||||||
"no-eq-null": "error",
|
"no-eq-null": 2,
|
||||||
"no-eval": "error",
|
"no-eval": 2,
|
||||||
"no-ex-assign": "error",
|
"no-ex-assign": 2,
|
||||||
"no-extra-parens": ["error", "all"],
|
"no-extra-parens": ["error", "all"],
|
||||||
"no-extra-semi": "error",
|
"no-extra-semi": 2,
|
||||||
"no-fallthrough": "error",
|
"no-fallthrough": 2,
|
||||||
"no-floating-decimal": "error",
|
"no-floating-decimal": 2,
|
||||||
"no-func-assign": "error",
|
"no-func-assign": 2,
|
||||||
"no-implied-eval": "error",
|
"no-implied-eval": 2,
|
||||||
"no-iterator": "error",
|
"no-iterator": "error",
|
||||||
"no-label-var": "error",
|
"no-label-var": 2,
|
||||||
"no-labels": "error",
|
"no-labels": 2,
|
||||||
"no-loop-func": "error",
|
"no-loop-func": 2,
|
||||||
"no-mixed-requires": [ "off", false ],
|
"no-mixed-requires": [ 0, false ],
|
||||||
"no-multi-str": "error",
|
"no-multi-str": 2,
|
||||||
"no-native-reassign": "error",
|
"no-native-reassign": 2,
|
||||||
"no-nested-ternary": "off",
|
"no-nested-ternary": 0,
|
||||||
"no-new-func": "error",
|
"no-new-func": "error",
|
||||||
"no-new-object": "error",
|
"no-new-object": 2,
|
||||||
"no-new-wrappers": "error",
|
"no-new-wrappers": 2,
|
||||||
"no-obj-calls": "error",
|
"no-obj-calls": 2,
|
||||||
"no-octal": "error",
|
"no-octal": 2,
|
||||||
"no-octal-escape": "error",
|
"no-octal-escape": 2,
|
||||||
"no-param-reassign": "off",
|
"no-param-reassign": 0,
|
||||||
"no-plusplus": "error",
|
"no-plusplus": 2,
|
||||||
"no-proto": "error",
|
"no-proto": 2,
|
||||||
"no-redeclare": "error",
|
"no-redeclare": 2,
|
||||||
"no-regex-spaces": "error",
|
"no-regex-spaces": 2,
|
||||||
"no-restricted-syntax": "off",
|
"no-restricted-syntax": 0,
|
||||||
"no-return-assign": "error",
|
"no-return-assign": 2,
|
||||||
"no-script-url": "error",
|
"no-script-url": 2,
|
||||||
"no-self-compare": "error",
|
"no-self-compare": 2,
|
||||||
"no-shadow": "off",
|
"no-shadow": 0,
|
||||||
"no-sync": "error",
|
"no-sync": 2,
|
||||||
"no-ternary": "off",
|
"no-ternary": 0,
|
||||||
"no-trailing-spaces": "error",
|
"no-trailing-spaces": 2,
|
||||||
"no-undef": "error",
|
"no-undef": "error",
|
||||||
"no-undef-init": "error",
|
"no-undef-init": 2,
|
||||||
"no-underscore-dangle": "off",
|
"no-underscore-dangle": 0,
|
||||||
"no-unneeded-ternary": [ "error", { "defaultAssignment": false } ],
|
"no-unneeded-ternary": [ "error", { "defaultAssignment": false } ],
|
||||||
"no-unreachable": "error",
|
"no-unreachable": 2,
|
||||||
"no-unused-expressions": "error",
|
"no-unused-expressions": 2,
|
||||||
"no-unused-vars": [ "error",
|
"no-unused-vars": [ "error",
|
||||||
{
|
{
|
||||||
"vars": "local",
|
"vars": "local",
|
||||||
@@ -361,19 +359,18 @@
|
|||||||
"varsIgnorePattern": "print_elapsed_time|check_duplicate_ids"
|
"varsIgnorePattern": "print_elapsed_time|check_duplicate_ids"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"no-use-before-define": "error",
|
"no-use-before-define": 2,
|
||||||
"no-useless-constructor": "error",
|
"no-useless-constructor": "error",
|
||||||
// The Zulip codebase complies partially with the "no-useless-escape"
|
// The Zulip codebase complies partially with the "no-useless-escape"
|
||||||
// rule; only regex expressions haven't been updated yet.
|
// rule; only regex expressions haven't been updated yet.
|
||||||
// Updated regex expressions are currently being tested in casper
|
// Updated regex expressions are currently being tested in casper
|
||||||
// files and will decide about a potential future enforcement of this rule.
|
// files and will decide about a potential future enforcement of this rule.
|
||||||
"no-useless-escape": "off",
|
"no-useless-escape": 0,
|
||||||
"no-var": "error",
|
"space-unary-ops": 2,
|
||||||
"space-unary-ops": "error",
|
"no-whitespace-before-property": 2,
|
||||||
"no-whitespace-before-property": "error",
|
"no-with": 2,
|
||||||
"no-with": "error",
|
|
||||||
"one-var": [ "error", "never" ],
|
"one-var": [ "error", "never" ],
|
||||||
"padded-blocks": "off",
|
"padded-blocks": 0,
|
||||||
"prefer-const": [ "error",
|
"prefer-const": [ "error",
|
||||||
{
|
{
|
||||||
"destructuring": "any",
|
"destructuring": "any",
|
||||||
@@ -387,12 +384,11 @@
|
|||||||
"numbers": false
|
"numbers": false
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"quotes": [ "off", "single" ],
|
"quotes": [ 0, "single" ],
|
||||||
"radix": "error",
|
"radix": 2,
|
||||||
"semi": "error",
|
"semi": 2,
|
||||||
"semi-spacing": ["error", {"before": false, "after": true}],
|
"semi-spacing": [2, {"before": false, "after": true}],
|
||||||
"sort-imports": "error",
|
"space-before-blocks": 2,
|
||||||
"space-before-blocks": "error",
|
|
||||||
"space-before-function-paren": [ "error",
|
"space-before-function-paren": [ "error",
|
||||||
{
|
{
|
||||||
"anonymous": "always",
|
"anonymous": "always",
|
||||||
@@ -400,97 +396,16 @@
|
|||||||
"asyncArrow": "always"
|
"asyncArrow": "always"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"space-in-parens": "error",
|
"space-in-parens": 2,
|
||||||
"space-infix-ops": "error",
|
"space-infix-ops": 2,
|
||||||
"spaced-comment": "off",
|
"spaced-comment": 0,
|
||||||
"strict": "off",
|
"strict": 0,
|
||||||
"template-curly-spacing": "error",
|
"template-curly-spacing": "error",
|
||||||
"unnecessary-strict": "off",
|
"unnecessary-strict": 0,
|
||||||
"use-isnan": "error",
|
"use-isnan": 2,
|
||||||
"valid-typeof": [ "error", { "requireStringLiterals": true } ],
|
"valid-typeof": [ "error", { "requireStringLiterals": true } ],
|
||||||
"wrap-iife": [ "error", "outside", { "functionPrototypeMethods": false } ],
|
"wrap-iife": [ "error", "outside", { "functionPrototypeMethods": false } ],
|
||||||
"wrap-regex": "off",
|
"wrap-regex": 0,
|
||||||
"yoda": "error"
|
"yoda": 2
|
||||||
},
|
}
|
||||||
"overrides": [
|
|
||||||
{
|
|
||||||
"files": [
|
|
||||||
"frontend_tests/casper_tests/*.js",
|
|
||||||
"frontend_tests/casper_lib/*.js"
|
|
||||||
],
|
|
||||||
"rules": {
|
|
||||||
"no-var": "off" // PhantomJS doesn’t support let, const
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"files": ["**/*.ts"],
|
|
||||||
"parser": "@typescript-eslint/parser",
|
|
||||||
"parserOptions": {
|
|
||||||
"project": "tsconfig.json"
|
|
||||||
},
|
|
||||||
"plugins": ["@typescript-eslint"],
|
|
||||||
"rules": {
|
|
||||||
// Disable base rule to avoid conflict
|
|
||||||
"empty-returns/main": "off",
|
|
||||||
"indent": "off",
|
|
||||||
"func-call-spacing": "off",
|
|
||||||
"no-magic-numbers": "off",
|
|
||||||
"semi": "off",
|
|
||||||
"no-unused-vars": "off",
|
|
||||||
"no-useless-constructor": "off",
|
|
||||||
|
|
||||||
"@typescript-eslint/adjacent-overload-signatures": "error",
|
|
||||||
"@typescript-eslint/array-type": "error",
|
|
||||||
"@typescript-eslint/await-thenable": "error",
|
|
||||||
"@typescript-eslint/ban-types": "error",
|
|
||||||
"@typescript-eslint/ban-ts-ignore": "off",
|
|
||||||
"@typescript-eslint/camelcase": "off",
|
|
||||||
"@typescript-eslint/class-name-casing": "error",
|
|
||||||
"@typescript-eslint/consistent-type-assertions": "error",
|
|
||||||
"@typescript-eslint/explicit-function-return-type": ["error", { "allowExpressions": true }],
|
|
||||||
"@typescript-eslint/explicit-member-accessibility": "off",
|
|
||||||
"@typescript-eslint/func-call-spacing": "error",
|
|
||||||
"@typescript-eslint/generic-type-naming": "off",
|
|
||||||
"@typescript-eslint/indent": "error",
|
|
||||||
"@typescript-eslint/interface-name-prefix": "off",
|
|
||||||
"@typescript-eslint/member-delimiter-style": "error",
|
|
||||||
"@typescript-eslint/member-naming": ["error", { "private": "^_" } ],
|
|
||||||
"@typescript-eslint/member-ordering": "error",
|
|
||||||
"@typescript-eslint/no-array-constructor": "error",
|
|
||||||
"@typescript-eslint/no-empty-interface": "error",
|
|
||||||
"@typescript-eslint/no-explicit-any": "off",
|
|
||||||
"@typescript-eslint/no-extraneous-class": "error",
|
|
||||||
"@typescript-eslint/no-for-in-array": "off",
|
|
||||||
"@typescript-eslint/no-inferrable-types": "error",
|
|
||||||
"@typescript-eslint/no-magic-numbers": "off",
|
|
||||||
"@typescript-eslint/no-misused-new": "error",
|
|
||||||
"@typescript-eslint/no-namespace": "error",
|
|
||||||
"@typescript-eslint/no-non-null-assertion": "off",
|
|
||||||
"@typescript-eslint/no-parameter-properties": "error",
|
|
||||||
"@typescript-eslint/no-require-imports": "off",
|
|
||||||
"@typescript-eslint/no-this-alias": "off",
|
|
||||||
"@typescript-eslint/no-type-alias": "off",
|
|
||||||
"@typescript-eslint/no-unnecessary-qualifier": "error",
|
|
||||||
"@typescript-eslint/no-unnecessary-type-assertion": "error",
|
|
||||||
"@typescript-eslint/no-unused-vars": ["error", { "varsIgnorePattern": "^_" } ],
|
|
||||||
"@typescript-eslint/no-use-before-define": "error",
|
|
||||||
"@typescript-eslint/no-useless-constructor": "error",
|
|
||||||
"@typescript-eslint/no-var-requires": "off",
|
|
||||||
"@typescript-eslint/prefer-for-of": "off",
|
|
||||||
"@typescript-eslint/prefer-function-type": "off",
|
|
||||||
"@typescript-eslint/prefer-includes": "error",
|
|
||||||
"@typescript-eslint/prefer-interface": "off",
|
|
||||||
"@typescript-eslint/prefer-namespace-keyword": "error",
|
|
||||||
"@typescript-eslint/prefer-regexp-exec": "error",
|
|
||||||
"@typescript-eslint/prefer-string-starts-ends-with": "error",
|
|
||||||
"@typescript-eslint/promise-function-async": "error",
|
|
||||||
"@typescript-eslint/restrict-plus-operands": "off",
|
|
||||||
"@typescript-eslint/semi": "error",
|
|
||||||
"@typescript-eslint/triple-slash-reference": "error",
|
|
||||||
"@typescript-eslint/type-annotation-spacing": "error",
|
|
||||||
"@typescript-eslint/unbound-method": "off",
|
|
||||||
"@typescript-eslint/unified-signatures": "error"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|||||||
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -11,3 +11,4 @@
|
|||||||
*.otf binary
|
*.otf binary
|
||||||
*.tif binary
|
*.tif binary
|
||||||
*.ogg binary
|
*.ogg binary
|
||||||
|
yarn.lock binary
|
||||||
|
|||||||
7
.gitignore
vendored
7
.gitignore
vendored
@@ -34,12 +34,6 @@ package-lock.json
|
|||||||
# Dockerfiles generated for CircleCI
|
# Dockerfiles generated for CircleCI
|
||||||
/tools/circleci/images
|
/tools/circleci/images
|
||||||
|
|
||||||
# Generated i18n data
|
|
||||||
/locale/en
|
|
||||||
/locale/language_options.json
|
|
||||||
/locale/language_name_map.json
|
|
||||||
/locale/*/mobile.json
|
|
||||||
|
|
||||||
# Static build
|
# Static build
|
||||||
*.mo
|
*.mo
|
||||||
npm-debug.log
|
npm-debug.log
|
||||||
@@ -48,7 +42,6 @@ npm-debug.log
|
|||||||
/staticfiles.json
|
/staticfiles.json
|
||||||
/webpack-stats-production.json
|
/webpack-stats-production.json
|
||||||
/yarn-error.log
|
/yarn-error.log
|
||||||
zulip-git-version
|
|
||||||
|
|
||||||
# Test / analysis tools
|
# Test / analysis tools
|
||||||
.coverage
|
.coverage
|
||||||
|
|||||||
11
.travis.yml
11
.travis.yml
@@ -1,6 +1,7 @@
|
|||||||
# See https://zulip.readthedocs.io/en/latest/testing/continuous-integration.html for
|
# See https://zulip.readthedocs.io/en/latest/testing/continuous-integration.html for
|
||||||
# high-level documentation on our Travis CI setup.
|
# high-level documentation on our Travis CI setup.
|
||||||
dist: xenial
|
dist: trusty
|
||||||
|
group: deprecated-2017Q4
|
||||||
install:
|
install:
|
||||||
# Disable sometimes-broken sources.list in Travis base images
|
# Disable sometimes-broken sources.list in Travis base images
|
||||||
- sudo rm -vf /etc/apt/sources.list.d/*
|
- sudo rm -vf /etc/apt/sources.list.d/*
|
||||||
@@ -39,15 +40,15 @@ env:
|
|||||||
global:
|
global:
|
||||||
- BOTO_CONFIG=/nonexistent
|
- BOTO_CONFIG=/nonexistent
|
||||||
language: python
|
language: python
|
||||||
# Our test suites generally run on Python 3.5, the version in
|
# Our test suites generally run on Python 3.4, the version in
|
||||||
# Ubuntu 16.04 xenial, which is the oldest OS release we support.
|
# Ubuntu 14.04 trusty, which is the oldest OS release we support.
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
# Travis will actually run the jobs in the order they're listed here;
|
# Travis will actually run the jobs in the order they're listed here;
|
||||||
# that doesn't seem to be documented, but it's what we see empirically.
|
# that doesn't seem to be documented, but it's what we see empirically.
|
||||||
# We only get 4 jobs running at a time, so we try to make the first few
|
# We only get 4 jobs running at a time, so we try to make the first few
|
||||||
# the most likely to break.
|
# the most likely to break.
|
||||||
- python: "3.5"
|
- python: "3.4"
|
||||||
env: TEST_SUITE=production
|
env: TEST_SUITE=production
|
||||||
# Other suites moved to CircleCI -- see .circleci/.
|
# Other suites moved to CircleCI -- see .circleci/.
|
||||||
sudo: required
|
sudo: required
|
||||||
@@ -58,7 +59,7 @@ addons:
|
|||||||
# debugging test flakes.
|
# debugging test flakes.
|
||||||
- $(ls var/casper/* | tr "\n" ":")
|
- $(ls var/casper/* | tr "\n" ":")
|
||||||
- $(ls /tmp/zulip-test-event-log/* | tr "\n" ":")
|
- $(ls /tmp/zulip-test-event-log/* | tr "\n" ":")
|
||||||
postgresql: "9.5"
|
postgresql: "9.3"
|
||||||
apt:
|
apt:
|
||||||
packages:
|
packages:
|
||||||
- moreutils
|
- moreutils
|
||||||
|
|||||||
20
.tx/config
20
.tx/config
@@ -3,31 +3,31 @@ host = https://www.transifex.com
|
|||||||
lang_map = zh-Hans: zh_Hans, zh-Hant: zh_Hant
|
lang_map = zh-Hans: zh_Hans, zh-Hant: zh_Hant
|
||||||
|
|
||||||
[zulip.djangopo]
|
[zulip.djangopo]
|
||||||
file_filter = locale/<lang>/LC_MESSAGES/django.po
|
source_file = static/locale/en/LC_MESSAGES/django.po
|
||||||
source_file = locale/en/LC_MESSAGES/django.po
|
|
||||||
source_lang = en
|
source_lang = en
|
||||||
type = PO
|
type = PO
|
||||||
|
file_filter = static/locale/<lang>/LC_MESSAGES/django.po
|
||||||
|
|
||||||
[zulip.translationsjson]
|
[zulip.translationsjson]
|
||||||
file_filter = locale/<lang>/translations.json
|
source_file = static/locale/en/translations.json
|
||||||
source_file = locale/en/translations.json
|
|
||||||
source_lang = en
|
source_lang = en
|
||||||
type = KEYVALUEJSON
|
type = KEYVALUEJSON
|
||||||
|
file_filter = static/locale/<lang>/translations.json
|
||||||
|
|
||||||
[zulip.mobile]
|
[zulip.mobile]
|
||||||
file_filter = locale/<lang>/mobile.json
|
source_file = static/locale/en/mobile.json
|
||||||
source_file = locale/en/mobile.json
|
|
||||||
source_lang = en
|
source_lang = en
|
||||||
type = KEYVALUEJSON
|
type = KEYVALUEJSON
|
||||||
|
file_filter = static/locale/<lang>/mobile.json
|
||||||
|
|
||||||
[zulip-test.djangopo]
|
[zulip-test.djangopo]
|
||||||
file_filter = locale/<lang>/LC_MESSAGES/django.po
|
source_file = static/locale/en/LC_MESSAGES/django.po
|
||||||
source_file = locale/en/LC_MESSAGES/django.po
|
|
||||||
source_lang = en
|
source_lang = en
|
||||||
type = PO
|
type = PO
|
||||||
|
file_filter = static/locale/<lang>/LC_MESSAGES/django.po
|
||||||
|
|
||||||
[zulip-test.translationsjson]
|
[zulip-test.translationsjson]
|
||||||
file_filter = locale/<lang>/translations.json
|
source_file = static/locale/en/translations.json
|
||||||
source_file = locale/en/translations.json
|
|
||||||
source_lang = en
|
source_lang = en
|
||||||
type = KEYVALUEJSON
|
type = KEYVALUEJSON
|
||||||
|
file_filter = static/locale/<lang>/translations.json
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ something you can do while a violation is happening, do it. A lot of the
|
|||||||
harms of harassment and other violations can be mitigated by the victim
|
harms of harassment and other violations can be mitigated by the victim
|
||||||
knowing that the other people present are on their side.
|
knowing that the other people present are on their side.
|
||||||
|
|
||||||
All reports will be kept confidential. In some cases, we may determine that a
|
All reports will be kept confidential. In some cases we may determine that a
|
||||||
public statement will need to be made. In such cases, the identities of all
|
public statement will need to be made. In such cases, the identities of all
|
||||||
victims and reporters will remain confidential unless those individuals
|
victims and reporters will remain confidential unless those individuals
|
||||||
instruct us otherwise.
|
instruct us otherwise.
|
||||||
|
|||||||
@@ -13,8 +13,7 @@ user, or anything else. Make sure to read the
|
|||||||
before posting. The Zulip community is also governed by a
|
before posting. The Zulip community is also governed by a
|
||||||
[code of conduct](https://zulip.readthedocs.io/en/latest/code-of-conduct.html).
|
[code of conduct](https://zulip.readthedocs.io/en/latest/code-of-conduct.html).
|
||||||
|
|
||||||
You can subscribe to zulip-devel-announce@googlegroups.com or our
|
You can subscribe to zulip-devel@googlegroups.com for a lower traffic (~1
|
||||||
[Twitter](https://twitter.com/zulip) account for a lower traffic (~1
|
|
||||||
email/month) way to hear about things like mentorship opportunities with Google
|
email/month) way to hear about things like mentorship opportunities with Google
|
||||||
Code-in, in-person sprints at conferences, and other opportunities to
|
Code-in, in-person sprints at conferences, and other opportunities to
|
||||||
contribute.
|
contribute.
|
||||||
@@ -29,7 +28,7 @@ needs doing:
|
|||||||
[backend](https://github.com/zulip/zulip), web
|
[backend](https://github.com/zulip/zulip), web
|
||||||
[frontend](https://github.com/zulip/zulip), React Native
|
[frontend](https://github.com/zulip/zulip), React Native
|
||||||
[mobile app](https://github.com/zulip/zulip-mobile), or Electron
|
[mobile app](https://github.com/zulip/zulip-mobile), or Electron
|
||||||
[desktop app](https://github.com/zulip/zulip-desktop).
|
[desktop app](https://github.com/zulip/zulip-electron).
|
||||||
* Building out our
|
* Building out our
|
||||||
[Python API and bots](https://github.com/zulip/python-zulip-api) framework.
|
[Python API and bots](https://github.com/zulip/python-zulip-api) framework.
|
||||||
* [Writing an integration](https://zulipchat.com/api/integrations-overview).
|
* [Writing an integration](https://zulipchat.com/api/integrations-overview).
|
||||||
@@ -86,19 +85,17 @@ on.
|
|||||||
|
|
||||||
* If you're interested in
|
* If you're interested in
|
||||||
[mobile](https://github.com/zulip/zulip-mobile/issues?q=is%3Aopen+is%3Aissue),
|
[mobile](https://github.com/zulip/zulip-mobile/issues?q=is%3Aopen+is%3Aissue),
|
||||||
[desktop](https://github.com/zulip/zulip-desktop/issues?q=is%3Aopen+is%3Aissue),
|
[desktop](https://github.com/zulip/zulip-electron/issues?q=is%3Aopen+is%3Aissue),
|
||||||
or
|
or
|
||||||
[bots](https://github.com/zulip/python-zulip-api/issues?q=is%3Aopen+is%3Aissue)
|
[bots](https://github.com/zulip/python-zulip-api/issues?q=is%3Aopen+is%3Aissue)
|
||||||
development, check the respective links for open issues, or post in
|
development, check the respective links for open issues, or post in
|
||||||
[#mobile](https://chat.zulip.org/#narrow/stream/48-mobile),
|
[#mobile](https://chat.zulip.org/#narrow/stream/48-mobile),
|
||||||
[#desktop](https://chat.zulip.org/#narrow/stream/16-desktop), or
|
[#desktop](https://chat.zulip.org/#narrow/stream/16-desktop), or
|
||||||
[#integration](https://chat.zulip.org/#narrow/stream/127-integrations).
|
[#integration](https://chat.zulip.org/#narrow/stream/127-integrations).
|
||||||
* For the main server and web repository, we recommend browsing
|
* For the main server and web repository, start by looking through issues
|
||||||
recently opened issues to look for issues you are confident you can
|
with the label
|
||||||
fix correctly in a way that clearly communicates why your changes
|
[good first issue](https://github.com/zulip/zulip/issues?q=is%3Aopen+is%3Aissue+label%3A"good+first+issue").
|
||||||
are the correct fix. Our GitHub workflow bot, zulipbot, limits
|
These are smaller projects particularly suitable for a first contribution.
|
||||||
users who have 0 commits merged to claiming a single issue labeled
|
|
||||||
with "good first issue" or "help wanted".
|
|
||||||
* We also partition all of our issues in the main repo into areas like
|
* We also partition all of our issues in the main repo into areas like
|
||||||
admin, compose, emoji, hotkeys, i18n, onboarding, search, etc. Look
|
admin, compose, emoji, hotkeys, i18n, onboarding, search, etc. Look
|
||||||
through our [list of labels](https://github.com/zulip/zulip/labels), and
|
through our [list of labels](https://github.com/zulip/zulip/labels), and
|
||||||
@@ -108,8 +105,8 @@ on.
|
|||||||
[#new members](https://chat.zulip.org/#narrow/stream/95-new-members) with a
|
[#new members](https://chat.zulip.org/#narrow/stream/95-new-members) with a
|
||||||
bit about your background and interests, and we'll help you out. The most
|
bit about your background and interests, and we'll help you out. The most
|
||||||
important thing to say is whether you're looking for a backend (Python),
|
important thing to say is whether you're looking for a backend (Python),
|
||||||
frontend (JavaScript and TypeScript), mobile (React Native), desktop (Electron),
|
frontend (JavaScript), mobile (React Native), desktop (Electron),
|
||||||
documentation (English) or visual design (JavaScript/TypeScript + CSS) issue, and a
|
documentation (English) or visual design (JavaScript + CSS) issue, and a
|
||||||
bit about your programming experience and available time.
|
bit about your programming experience and available time.
|
||||||
|
|
||||||
We also welcome suggestions of features that you feel would be valuable or
|
We also welcome suggestions of features that you feel would be valuable or
|
||||||
@@ -122,17 +119,11 @@ Other notes:
|
|||||||
* For a first pull request, it's better to aim for a smaller contribution
|
* For a first pull request, it's better to aim for a smaller contribution
|
||||||
than a bigger one. Many first contributions have fewer than 10 lines of
|
than a bigger one. Many first contributions have fewer than 10 lines of
|
||||||
changes (not counting changes to tests).
|
changes (not counting changes to tests).
|
||||||
* The full list of issues explicitly looking for a contributor can be
|
* The full list of issues looking for a contributor can be found with the
|
||||||
found with the
|
|
||||||
[good first issue](https://github.com/zulip/zulip/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)
|
[good first issue](https://github.com/zulip/zulip/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)
|
||||||
and
|
and
|
||||||
[help wanted](https://github.com/zulip/zulip/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22)
|
[help wanted](https://github.com/zulip/zulip/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22)
|
||||||
labels. Avoid issues with the "difficult" label unless you
|
labels.
|
||||||
understand why it is difficult and are confident you can resolve the
|
|
||||||
issue correctly and completely. Issues without one of these labels
|
|
||||||
are fair game if Tim has written a clear technical design proposal
|
|
||||||
in the issue, or it is a bug that you can reproduce and you are
|
|
||||||
confident you can fix the issue correctly.
|
|
||||||
* For most new contributors, there's a lot to learn while making your first
|
* For most new contributors, there's a lot to learn while making your first
|
||||||
pull request. It's OK if it takes you a while; that's normal! You'll be
|
pull request. It's OK if it takes you a while; that's normal! You'll be
|
||||||
able to work a lot faster as you build experience.
|
able to work a lot faster as you build experience.
|
||||||
@@ -278,10 +269,10 @@ quickly become core team members. We hope you apply!
|
|||||||
|
|
||||||
### Google Summer of Code
|
### Google Summer of Code
|
||||||
|
|
||||||
GSoC is by far the largest of our internship programs (14 students in
|
GSoC is by far the largest of our internship programs (we had 14 GSoC
|
||||||
2017; 11 in 2018; 17 in 2019). While we don't control how many slots
|
students in summer 2017). While we don't control how many slots
|
||||||
Google allocates to Zulip, we hope to mentor a similar number of
|
Google allocates to Zulip, we hope to mentor a similar number of
|
||||||
students in future summers.
|
students in 2018.
|
||||||
|
|
||||||
If you're reading this well before the application deadline and want
|
If you're reading this well before the application deadline and want
|
||||||
to make your application strong, we recommend getting involved in the
|
to make your application strong, we recommend getting involved in the
|
||||||
@@ -319,7 +310,7 @@ list typically takes about 15 minutes.
|
|||||||
* Star us on GitHub. There are four main repositories:
|
* Star us on GitHub. There are four main repositories:
|
||||||
[server/web](https://github.com/zulip/zulip),
|
[server/web](https://github.com/zulip/zulip),
|
||||||
[mobile](https://github.com/zulip/zulip-mobile),
|
[mobile](https://github.com/zulip/zulip-mobile),
|
||||||
[desktop](https://github.com/zulip/zulip-desktop), and
|
[desktop](https://github.com/zulip/zulip-electron), and
|
||||||
[Python API](https://github.com/zulip/python-zulip-api).
|
[Python API](https://github.com/zulip/python-zulip-api).
|
||||||
* [Follow us](https://twitter.com/zulip) on Twitter.
|
* [Follow us](https://twitter.com/zulip) on Twitter.
|
||||||
|
|
||||||
|
|||||||
17
Dockerfile-dev
Normal file
17
Dockerfile-dev
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
FROM ubuntu:trusty
|
||||||
|
|
||||||
|
EXPOSE 9991
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y wget
|
||||||
|
|
||||||
|
RUN localedef -i en_US -f UTF-8 en_US.UTF-8
|
||||||
|
|
||||||
|
RUN useradd -d /home/zulip -m zulip && echo 'zulip ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
|
||||||
|
|
||||||
|
USER zulip
|
||||||
|
|
||||||
|
RUN ln -nsf /srv/zulip ~/zulip
|
||||||
|
RUN echo 'export LC_ALL="en_US.UTF-8" LANG="en_US.UTF-8" LANGUAGE="en_US.UTF-8"' >> ~zulip/.bashrc
|
||||||
|
RUN echo 'export LC_ALL="en_US.UTF-8" LANG="en_US.UTF-8" LANGUAGE="en_US.UTF-8"' >> ~zulip/.bash_profile
|
||||||
|
|
||||||
|
WORKDIR /srv/zulip
|
||||||
@@ -1,16 +1,33 @@
|
|||||||
# To build run `docker build -f Dockerfile-postgresql .` from the root of the
|
# To build run `docker build -f Dockerfile-postgresql .` from the root of the
|
||||||
# zulip repo.
|
# zulip repo.
|
||||||
|
|
||||||
# Currently the postgres images do not support automatic upgrading of
|
# Install build tools and build tsearch_extras for the current postgres
|
||||||
|
# version. Currently the postgres images do not support automatic upgrading of
|
||||||
# the on-disk data in volumes. So the base image can not currently be upgraded
|
# the on-disk data in volumes. So the base image can not currently be upgraded
|
||||||
# without users needing a manual pgdump and restore.
|
# without users needing a manual pgdump and restore.
|
||||||
|
FROM postgres:10
|
||||||
|
RUN apt-get update \
|
||||||
|
&& DEBIAN_FRONTEND=noninteractive apt-get install -y \
|
||||||
|
postgresql-server-dev-$PG_MAJOR \
|
||||||
|
postgresql-server-dev-all \
|
||||||
|
git \
|
||||||
|
build-essential \
|
||||||
|
fakeroot \
|
||||||
|
devscripts
|
||||||
|
RUN git clone https://github.com/zulip/tsearch_extras.git \
|
||||||
|
&& cd tsearch_extras \
|
||||||
|
&& echo $PG_MAJOR > debian/pgversions \
|
||||||
|
&& pg_buildext updatecontrol \
|
||||||
|
&& debuild -b -uc -us
|
||||||
|
|
||||||
# Install hunspell, zulip stop words, and run zulip database
|
# Install tsearch_extras, hunspell, zulip stop words, and run zulip database
|
||||||
# init.
|
# init.
|
||||||
FROM postgres:10
|
FROM postgres:10
|
||||||
|
ENV TSEARCH_EXTRAS_VERSION=0.4
|
||||||
|
ENV TSEARCH_EXTRAS_DEB=postgresql-${PG_MAJOR}-tsearch-extras_${TSEARCH_EXTRAS_VERSION}_amd64.deb
|
||||||
|
COPY --from=0 /${TSEARCH_EXTRAS_DEB} /tmp
|
||||||
COPY puppet/zulip/files/postgresql/zulip_english.stop /usr/share/postgresql/$PG_MAJOR/tsearch_data/zulip_english.stop
|
COPY puppet/zulip/files/postgresql/zulip_english.stop /usr/share/postgresql/$PG_MAJOR/tsearch_data/zulip_english.stop
|
||||||
COPY scripts/setup/create-db.sql /docker-entrypoint-initdb.d/zulip-create-db.sql
|
COPY scripts/setup/postgres-create-db /docker-entrypoint-initdb.d/postgres-create-db.sh
|
||||||
COPY scripts/setup/create-pgroonga.sql /docker-entrypoint-initdb.d/zulip-create-pgroonga.sql
|
|
||||||
COPY scripts/setup/pgroonga-debian.asc /tmp
|
COPY scripts/setup/pgroonga-debian.asc /tmp
|
||||||
RUN apt-key add /tmp/pgroonga-debian.asc \
|
RUN apt-key add /tmp/pgroonga-debian.asc \
|
||||||
&& echo "deb http://packages.groonga.org/debian/ stretch main" > /etc/apt/sources.list.d/zulip.list \
|
&& echo "deb http://packages.groonga.org/debian/ stretch main" > /etc/apt/sources.list.d/zulip.list \
|
||||||
@@ -18,6 +35,8 @@ RUN apt-key add /tmp/pgroonga-debian.asc \
|
|||||||
&& DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y \
|
&& DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y \
|
||||||
hunspell-en-us \
|
hunspell-en-us \
|
||||||
postgresql-${PG_MAJOR}-pgroonga \
|
postgresql-${PG_MAJOR}-pgroonga \
|
||||||
|
&& DEBIAN_FRONTEND=noninteractive dpkg -i /tmp/${TSEARCH_EXTRAS_DEB} \
|
||||||
|
&& rm /tmp/${TSEARCH_EXTRAS_DEB} \
|
||||||
&& ln -sf /var/cache/postgresql/dicts/en_us.dict "/usr/share/postgresql/$PG_MAJOR/tsearch_data/en_us.dict" \
|
&& ln -sf /var/cache/postgresql/dicts/en_us.dict "/usr/share/postgresql/$PG_MAJOR/tsearch_data/en_us.dict" \
|
||||||
&& ln -sf /var/cache/postgresql/dicts/en_us.affix "/usr/share/postgresql/$PG_MAJOR/tsearch_data/en_us.affix" \
|
&& ln -sf /var/cache/postgresql/dicts/en_us.affix "/usr/share/postgresql/$PG_MAJOR/tsearch_data/en_us.affix" \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|||||||
16
README.md
16
README.md
@@ -8,8 +8,9 @@ allows users to easily process hundreds or thousands of messages a day. With
|
|||||||
over 500 contributors merging over 500 commits a month, Zulip is also the
|
over 500 contributors merging over 500 commits a month, Zulip is also the
|
||||||
largest and fastest growing open source group chat project.
|
largest and fastest growing open source group chat project.
|
||||||
|
|
||||||
[](https://circleci.com/gh/zulip/zulip/tree/master)
|
[](https://circleci.com/gh/zulip/zulip)
|
||||||
[](https://codecov.io/gh/zulip/zulip/branch/master)
|
[](https://travis-ci.org/zulip/zulip)
|
||||||
|
[](https://codecov.io/gh/zulip/zulip)
|
||||||
[][mypy-coverage]
|
[][mypy-coverage]
|
||||||
[](https://github.com/zulip/zulip/releases/latest)
|
[](https://github.com/zulip/zulip/releases/latest)
|
||||||
[](https://zulip.readthedocs.io/en/latest/)
|
[](https://zulip.readthedocs.io/en/latest/)
|
||||||
@@ -55,11 +56,12 @@ You might be interested in:
|
|||||||
[companies](https://zulipchat.com/for/companies/), or Zulip for
|
[companies](https://zulipchat.com/for/companies/), or Zulip for
|
||||||
[working groups and part time communities](https://zulipchat.com/for/working-groups-and-communities/).
|
[working groups and part time communities](https://zulipchat.com/for/working-groups-and-communities/).
|
||||||
|
|
||||||
* **Running a Zulip server**. Use a preconfigured [Digital Ocean droplet](https://marketplace.digitalocean.com/apps/zulip),
|
* **Running a Zulip server**. Setting up a server takes just a couple
|
||||||
[install Zulip](https://zulip.readthedocs.io/en/stable/production/install.html)
|
of minutes. Zulip runs on Ubuntu 18.04 Bionic, Ubuntu 16.04 Xenial,
|
||||||
directly, or use Zulip's
|
Ubuntu 14.04 Trusty, and Debian 9 Stretch. The installation process is
|
||||||
experimental [Docker image](https://zulip.readthedocs.io/en/latest/production/deployment.html#zulip-in-docker).
|
[documented here](https://zulip.readthedocs.io/en/stable/production/install.html).
|
||||||
Commercial support is available; see <https://zulipchat.com/plans> for details.
|
Commercial support is available; see <https://zulipchat.com/plans>
|
||||||
|
for details.
|
||||||
|
|
||||||
* **Using Zulip without setting up a server**. <https://zulipchat.com> offers
|
* **Using Zulip without setting up a server**. <https://zulipchat.com> offers
|
||||||
free and commercial hosting.
|
free and commercial hosting.
|
||||||
|
|||||||
135
Vagrantfile
vendored
135
Vagrantfile
vendored
@@ -19,6 +19,43 @@ if Vagrant::VERSION == "1.8.7" then
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Workaround: the lxc-config in vagrant-lxc is incompatible with changes in
|
||||||
|
# LXC 2.1.0, found in Ubuntu 17.10 artful. LXC 2.1.1 (in 18.04 LTS bionic)
|
||||||
|
# ignores the old config key, so this will only be needed for artful.
|
||||||
|
#
|
||||||
|
# vagrant-lxc upstream has an attempted fix:
|
||||||
|
# https://github.com/fgrehm/vagrant-lxc/issues/445
|
||||||
|
# but it didn't work in our testing. This is a temporary issue, so we just
|
||||||
|
# hack in a fix: we patch the skeleton `lxc-config` file right in the
|
||||||
|
# distribution of the vagrant-lxc "box" we use. If the user doesn't yet
|
||||||
|
# have the box (e.g. on first setup), Vagrant would download it but too
|
||||||
|
# late for us to patch it like this; so we prompt them to explicitly add it
|
||||||
|
# first and then rerun.
|
||||||
|
if ['up', 'provision'].include? ARGV[0]
|
||||||
|
if command? "lxc-ls"
|
||||||
|
LXC_VERSION = `lxc-ls --version`.strip unless defined? LXC_VERSION
|
||||||
|
if LXC_VERSION == "2.1.0"
|
||||||
|
lxc_config_file = ENV['HOME'] + "/.vagrant.d/boxes/fgrehm-VAGRANTSLASH-trusty64-lxc/1.2.0/lxc/lxc-config"
|
||||||
|
if File.file?(lxc_config_file)
|
||||||
|
lines = File.readlines(lxc_config_file)
|
||||||
|
deprecated_line = "lxc.pivotdir = lxc_putold\n"
|
||||||
|
if lines[1] == deprecated_line
|
||||||
|
lines[1] = "# #{deprecated_line}"
|
||||||
|
File.open(lxc_config_file, 'w') do |f|
|
||||||
|
f.puts(lines)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
puts 'You are running LXC 2.1.0, and fgrehm/trusty64-lxc box is incompatible '\
|
||||||
|
"with it by default. First add the box by doing:\n"\
|
||||||
|
" vagrant box add https://vagrantcloud.com/fgrehm/trusty64-lxc\n"\
|
||||||
|
'Once this command succeeds, do "vagrant up" again.'
|
||||||
|
exit
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Workaround: Vagrant removed the atlas.hashicorp.com to
|
# Workaround: Vagrant removed the atlas.hashicorp.com to
|
||||||
# vagrantcloud.com redirect in February 2018. The value of
|
# vagrantcloud.com redirect in February 2018. The value of
|
||||||
# DEFAULT_SERVER_URL in Vagrant versions less than 1.9.3 is
|
# DEFAULT_SERVER_URL in Vagrant versions less than 1.9.3 is
|
||||||
@@ -29,38 +66,24 @@ if Vagrant::DEFAULT_SERVER_URL == "atlas.hashicorp.com"
|
|||||||
Vagrant::DEFAULT_SERVER_URL.replace('https://vagrantcloud.com')
|
Vagrant::DEFAULT_SERVER_URL.replace('https://vagrantcloud.com')
|
||||||
end
|
end
|
||||||
|
|
||||||
# Monkey patch https://github.com/hashicorp/vagrant/pull/10879 so we
|
|
||||||
# can fall back to another provider if docker is not installed.
|
|
||||||
begin
|
|
||||||
require Vagrant.source_root.join("plugins", "providers", "docker", "provider")
|
|
||||||
rescue LoadError
|
|
||||||
else
|
|
||||||
VagrantPlugins::DockerProvider::Provider.class_eval do
|
|
||||||
method(:usable?).owner == singleton_class or def self.usable?(raise_error=false)
|
|
||||||
VagrantPlugins::DockerProvider::Driver.new.execute("docker", "version")
|
|
||||||
true
|
|
||||||
rescue Vagrant::Errors::CommandUnavailable, VagrantPlugins::DockerProvider::Errors::ExecuteError
|
|
||||||
raise if raise_error
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
||||||
|
|
||||||
|
# For LXC. VirtualBox hosts use a different box, described below.
|
||||||
|
config.vm.box = "fgrehm/trusty64-lxc"
|
||||||
|
|
||||||
# The Zulip development environment runs on 9991 on the guest.
|
# The Zulip development environment runs on 9991 on the guest.
|
||||||
host_port = 9991
|
host_port = 9991
|
||||||
http_proxy = https_proxy = no_proxy = nil
|
http_proxy = https_proxy = no_proxy = nil
|
||||||
host_ip_addr = "127.0.0.1"
|
host_ip_addr = "127.0.0.1"
|
||||||
|
|
||||||
# System settings for the virtual machine.
|
|
||||||
vm_num_cpus = "2"
|
|
||||||
vm_memory = "2048"
|
|
||||||
|
|
||||||
ubuntu_mirror = ""
|
|
||||||
|
|
||||||
config.vm.synced_folder ".", "/vagrant", disabled: true
|
config.vm.synced_folder ".", "/vagrant", disabled: true
|
||||||
config.vm.synced_folder ".", "/srv/zulip"
|
if (/darwin/ =~ RUBY_PLATFORM) != nil
|
||||||
|
config.vm.synced_folder ".", "/srv/zulip", type: "nfs",
|
||||||
|
linux__nfs_options: ['rw']
|
||||||
|
config.vm.network "private_network", type: "dhcp"
|
||||||
|
else
|
||||||
|
config.vm.synced_folder ".", "/srv/zulip"
|
||||||
|
end
|
||||||
|
|
||||||
vagrant_config_file = ENV['HOME'] + "/.zulip-vagrant-config"
|
vagrant_config_file = ENV['HOME'] + "/.zulip-vagrant-config"
|
||||||
if File.file?(vagrant_config_file)
|
if File.file?(vagrant_config_file)
|
||||||
@@ -74,9 +97,6 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
|||||||
when "NO_PROXY"; no_proxy = value
|
when "NO_PROXY"; no_proxy = value
|
||||||
when "HOST_PORT"; host_port = value.to_i
|
when "HOST_PORT"; host_port = value.to_i
|
||||||
when "HOST_IP_ADDR"; host_ip_addr = value
|
when "HOST_IP_ADDR"; host_ip_addr = value
|
||||||
when "GUEST_CPUS"; vm_num_cpus = value
|
|
||||||
when "GUEST_MEMORY_MB"; vm_memory = value
|
|
||||||
when "UBUNTU_MIRROR"; ubuntu_mirror = value
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -102,28 +122,35 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
|||||||
|
|
||||||
config.vm.network "forwarded_port", guest: 9991, host: host_port, host_ip: host_ip_addr
|
config.vm.network "forwarded_port", guest: 9991, host: host_port, host_ip: host_ip_addr
|
||||||
config.vm.network "forwarded_port", guest: 9994, host: host_port + 3, host_ip: host_ip_addr
|
config.vm.network "forwarded_port", guest: 9994, host: host_port + 3, host_ip: host_ip_addr
|
||||||
# Specify Docker provider before VirtualBox provider so it's preferred.
|
# Specify LXC provider before VirtualBox provider so it's preferred.
|
||||||
config.vm.provider "docker" do |d, override|
|
config.vm.provider "lxc" do |lxc|
|
||||||
d.build_dir = File.join(__dir__, "tools", "setup", "dev-vagrant-docker")
|
if command? "lxc-ls"
|
||||||
d.build_args = ["--build-arg", "VAGRANT_UID=#{Process.uid}"]
|
LXC_VERSION = `lxc-ls --version`.strip unless defined? LXC_VERSION
|
||||||
if !ubuntu_mirror.empty?
|
if LXC_VERSION >= "1.1.0" and LXC_VERSION < "3.0.0"
|
||||||
d.build_args += ["--build-arg", "UBUNTU_MIRROR=#{ubuntu_mirror}"]
|
# Allow start without AppArmor, otherwise Box will not Start on Ubuntu 14.10
|
||||||
|
# see https://github.com/fgrehm/vagrant-lxc/issues/333
|
||||||
|
lxc.customize 'aa_allow_incomplete', 1
|
||||||
|
end
|
||||||
|
if LXC_VERSION >= "3.0.0"
|
||||||
|
lxc.customize 'apparmor.allow_incomplete', 1
|
||||||
|
end
|
||||||
|
if LXC_VERSION >= "2.0.0"
|
||||||
|
lxc.backingstore = 'dir'
|
||||||
|
end
|
||||||
end
|
end
|
||||||
d.has_ssh = true
|
|
||||||
d.create_args = ["--ulimit", "nofile=1024:65536"]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
config.vm.provider "virtualbox" do |vb, override|
|
config.vm.provider "virtualbox" do |vb, override|
|
||||||
override.vm.box = "ubuntu/bionic64"
|
override.vm.box = "ubuntu/trusty64"
|
||||||
# An unnecessary log file gets generated when running vagrant up for the
|
|
||||||
# first time with the Ubuntu Bionic box. This looks like it is being
|
|
||||||
# caused upstream by the base box containing a Vagrantfile with a similar
|
|
||||||
# line to the one below.
|
|
||||||
# see https://github.com/hashicorp/vagrant/issues/9425
|
|
||||||
vb.customize [ "modifyvm", :id, "--uartmode1", "disconnected" ]
|
|
||||||
# It's possible we can get away with just 1.5GB; more testing needed
|
# It's possible we can get away with just 1.5GB; more testing needed
|
||||||
vb.memory = vm_memory
|
vb.memory = 2048
|
||||||
vb.cpus = vm_num_cpus
|
vb.cpus = 2
|
||||||
|
end
|
||||||
|
|
||||||
|
config.vm.provider "vmware_fusion" do |vb, override|
|
||||||
|
override.vm.box = "puphpet/ubuntu1404-x64"
|
||||||
|
vb.vmx["memsize"] = "2048"
|
||||||
|
vb.vmx["numvcpus"] = "2"
|
||||||
end
|
end
|
||||||
|
|
||||||
$provision_script = <<SCRIPT
|
$provision_script = <<SCRIPT
|
||||||
@@ -135,15 +162,19 @@ set -o pipefail
|
|||||||
# something that we don't want to happen when running provision in a
|
# something that we don't want to happen when running provision in a
|
||||||
# development environment not using Vagrant.
|
# development environment not using Vagrant.
|
||||||
|
|
||||||
# Set the Ubuntu mirror
|
|
||||||
[ ! '#{ubuntu_mirror}' ] || sudo sed -i 's|http://\\(\\w*\\.\\)*archive\\.ubuntu\\.com/ubuntu/\\? |#{ubuntu_mirror} |' /etc/apt/sources.list
|
|
||||||
|
|
||||||
# Set the MOTD on the system to have Zulip instructions
|
# Set the MOTD on the system to have Zulip instructions
|
||||||
sudo ln -nsf /srv/zulip/tools/setup/dev-motd /etc/update-motd.d/99-zulip-dev
|
sudo rm -f /etc/update-motd.d/*
|
||||||
sudo rm -f /etc/update-motd.d/10-help-text
|
sudo bash -c 'cat << EndOfMessage > /etc/motd
|
||||||
sudo dpkg --purge landscape-client landscape-common ubuntu-release-upgrader-core update-manager-core update-notifier-common ubuntu-server
|
Welcome to the Zulip development environment! Popular commands:
|
||||||
sudo dpkg-divert --add --rename /etc/default/motd-news
|
* tools/provision - Update the development environment
|
||||||
sudo sh -c 'echo ENABLED=0 > /etc/default/motd-news'
|
* tools/run-dev.py - Run the development server
|
||||||
|
* tools/lint - Run the linter (quick and catches many problems)
|
||||||
|
* tools/test-* - Run tests (use --help to learn about options)
|
||||||
|
|
||||||
|
Read https://zulip.readthedocs.io/en/latest/testing/testing.html to learn
|
||||||
|
how to run individual test suites so that you can get a fast debug cycle.
|
||||||
|
|
||||||
|
EndOfMessage'
|
||||||
|
|
||||||
# If the host is running SELinux remount the /sys/fs/selinux directory as read only,
|
# If the host is running SELinux remount the /sys/fs/selinux directory as read only,
|
||||||
# needed for apt-get to work.
|
# needed for apt-get to work.
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ from analytics.models import BaseCount, \
|
|||||||
from zerver.lib.logging_util import log_to_file
|
from zerver.lib.logging_util import log_to_file
|
||||||
from zerver.lib.timestamp import ceiling_to_day, \
|
from zerver.lib.timestamp import ceiling_to_day, \
|
||||||
ceiling_to_hour, floor_to_hour, verify_UTC
|
ceiling_to_hour, floor_to_hour, verify_UTC
|
||||||
from zerver.models import Message, Realm, RealmAuditLog, \
|
from zerver.models import Message, Realm, \
|
||||||
Stream, UserActivityInterval, UserProfile, models
|
Stream, UserActivityInterval, UserProfile, models
|
||||||
|
|
||||||
## Logging setup ##
|
## Logging setup ##
|
||||||
@@ -295,8 +295,8 @@ count_message_by_user_query = """
|
|||||||
zerver_userprofile.id = zerver_message.sender_id
|
zerver_userprofile.id = zerver_message.sender_id
|
||||||
WHERE
|
WHERE
|
||||||
zerver_userprofile.date_joined < %%(time_end)s AND
|
zerver_userprofile.date_joined < %%(time_end)s AND
|
||||||
zerver_message.date_sent >= %%(time_start)s AND
|
zerver_message.pub_date >= %%(time_start)s AND
|
||||||
zerver_message.date_sent < %%(time_end)s
|
zerver_message.pub_date < %%(time_end)s
|
||||||
GROUP BY zerver_userprofile.id %(group_by_clause)s
|
GROUP BY zerver_userprofile.id %(group_by_clause)s
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -322,8 +322,8 @@ count_message_type_by_user_query = """
|
|||||||
JOIN zerver_message
|
JOIN zerver_message
|
||||||
ON
|
ON
|
||||||
zerver_userprofile.id = zerver_message.sender_id AND
|
zerver_userprofile.id = zerver_message.sender_id AND
|
||||||
zerver_message.date_sent >= %%(time_start)s AND
|
zerver_message.pub_date >= %%(time_start)s AND
|
||||||
zerver_message.date_sent < %%(time_end)s
|
zerver_message.pub_date < %%(time_end)s
|
||||||
JOIN zerver_recipient
|
JOIN zerver_recipient
|
||||||
ON
|
ON
|
||||||
zerver_message.recipient_id = zerver_recipient.id
|
zerver_message.recipient_id = zerver_recipient.id
|
||||||
@@ -359,8 +359,8 @@ count_message_by_stream_query = """
|
|||||||
WHERE
|
WHERE
|
||||||
zerver_stream.date_created < %%(time_end)s AND
|
zerver_stream.date_created < %%(time_end)s AND
|
||||||
zerver_recipient.type = 2 AND
|
zerver_recipient.type = 2 AND
|
||||||
zerver_message.date_sent >= %%(time_start)s AND
|
zerver_message.pub_date >= %%(time_start)s AND
|
||||||
zerver_message.date_sent < %%(time_end)s
|
zerver_message.pub_date < %%(time_end)s
|
||||||
GROUP BY zerver_stream.id %(group_by_clause)s
|
GROUP BY zerver_stream.id %(group_by_clause)s
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -385,7 +385,7 @@ count_user_by_realm_query = """
|
|||||||
|
|
||||||
# Currently hardcodes the query needed for active_users_audit:is_bot:day.
|
# Currently hardcodes the query needed for active_users_audit:is_bot:day.
|
||||||
# Assumes that a user cannot have two RealmAuditLog entries with the same event_time and
|
# Assumes that a user cannot have two RealmAuditLog entries with the same event_time and
|
||||||
# event_type in [RealmAuditLog.USER_CREATED, USER_DEACTIVATED, etc].
|
# event_type in ['user_created', 'user_deactivated', etc].
|
||||||
# In particular, it's important to ensure that migrations don't cause that to happen.
|
# In particular, it's important to ensure that migrations don't cause that to happen.
|
||||||
check_realmauditlog_by_user_query = """
|
check_realmauditlog_by_user_query = """
|
||||||
INSERT INTO analytics_usercount
|
INSERT INTO analytics_usercount
|
||||||
@@ -397,7 +397,7 @@ check_realmauditlog_by_user_query = """
|
|||||||
SELECT modified_user_id, max(event_time) AS max_event_time
|
SELECT modified_user_id, max(event_time) AS max_event_time
|
||||||
FROM zerver_realmauditlog
|
FROM zerver_realmauditlog
|
||||||
WHERE
|
WHERE
|
||||||
event_type in ({user_created}, {user_activated}, {user_deactivated}, {user_reactivated}) AND
|
event_type in ('user_created', 'user_deactivated', 'user_activated', 'user_reactivated') AND
|
||||||
event_time < %%(time_end)s
|
event_time < %%(time_end)s
|
||||||
GROUP BY modified_user_id
|
GROUP BY modified_user_id
|
||||||
) ral2
|
) ral2
|
||||||
@@ -408,11 +408,8 @@ check_realmauditlog_by_user_query = """
|
|||||||
ON
|
ON
|
||||||
ral1.modified_user_id = zerver_userprofile.id
|
ral1.modified_user_id = zerver_userprofile.id
|
||||||
WHERE
|
WHERE
|
||||||
ral1.event_type in ({user_created}, {user_activated}, {user_reactivated})
|
ral1.event_type in ('user_created', 'user_activated', 'user_reactivated')
|
||||||
""".format(user_created=RealmAuditLog.USER_CREATED,
|
"""
|
||||||
user_activated=RealmAuditLog.USER_ACTIVATED,
|
|
||||||
user_deactivated=RealmAuditLog.USER_DEACTIVATED,
|
|
||||||
user_reactivated=RealmAuditLog.USER_REACTIVATED)
|
|
||||||
|
|
||||||
check_useractivityinterval_by_user_query = """
|
check_useractivityinterval_by_user_query = """
|
||||||
INSERT INTO analytics_usercount
|
INSERT INTO analytics_usercount
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ def compute_stats(log_level: int) -> None:
|
|||||||
one_week_ago = timestamp_to_datetime(time.time()) - datetime.timedelta(weeks=1)
|
one_week_ago = timestamp_to_datetime(time.time()) - datetime.timedelta(weeks=1)
|
||||||
mit_query = Message.objects.filter(sender__realm__string_id="zephyr",
|
mit_query = Message.objects.filter(sender__realm__string_id="zephyr",
|
||||||
recipient__type=Recipient.STREAM,
|
recipient__type=Recipient.STREAM,
|
||||||
date_sent__gt=one_week_ago)
|
pub_date__gt=one_week_ago)
|
||||||
for bot_sender_start in ["imap.", "rcmd.", "sys."]:
|
for bot_sender_start in ["imap.", "rcmd.", "sys."]:
|
||||||
mit_query = mit_query.exclude(sender__email__startswith=(bot_sender_start))
|
mit_query = mit_query.exclude(sender__email__startswith=(bot_sender_start))
|
||||||
# Filtering for "/" covers tabbott/extra@ and all the daemon/foo bots.
|
# Filtering for "/" covers tabbott/extra@ and all the daemon/foo bots.
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
|
import sys
|
||||||
from argparse import ArgumentParser
|
from argparse import ArgumentParser
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from django.core.management.base import BaseCommand, CommandError
|
from django.core.management.base import BaseCommand
|
||||||
|
|
||||||
from analytics.lib.counts import do_drop_all_analytics_tables
|
from analytics.lib.counts import do_drop_all_analytics_tables
|
||||||
|
|
||||||
@@ -17,4 +18,5 @@ class Command(BaseCommand):
|
|||||||
if options['force']:
|
if options['force']:
|
||||||
do_drop_all_analytics_tables()
|
do_drop_all_analytics_tables()
|
||||||
else:
|
else:
|
||||||
raise CommandError("Would delete all data from analytics tables (!); use --force to do so.")
|
print("Would delete all data from analytics tables (!); use --force to do so.")
|
||||||
|
sys.exit(1)
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
|
import sys
|
||||||
from argparse import ArgumentParser
|
from argparse import ArgumentParser
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from django.core.management.base import BaseCommand, CommandError
|
from django.core.management.base import BaseCommand
|
||||||
|
|
||||||
from analytics.lib.counts import COUNT_STATS, do_drop_single_stat
|
from analytics.lib.counts import COUNT_STATS, do_drop_single_stat
|
||||||
|
|
||||||
@@ -19,8 +20,10 @@ class Command(BaseCommand):
|
|||||||
def handle(self, *args: Any, **options: Any) -> None:
|
def handle(self, *args: Any, **options: Any) -> None:
|
||||||
property = options['property']
|
property = options['property']
|
||||||
if property not in COUNT_STATS:
|
if property not in COUNT_STATS:
|
||||||
raise CommandError("Invalid property: %s" % (property,))
|
print("Invalid property: %s" % (property,))
|
||||||
|
sys.exit(1)
|
||||||
if not options['force']:
|
if not options['force']:
|
||||||
raise CommandError("No action taken. Use --force.")
|
print("No action taken. Use --force.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
do_drop_single_stat(property)
|
do_drop_single_stat(property)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import datetime
|
import datetime
|
||||||
from argparse import ArgumentParser
|
from argparse import ArgumentParser
|
||||||
from typing import Any, Optional
|
from typing import Any
|
||||||
|
|
||||||
from django.db.models import Count, QuerySet
|
from django.db.models import Count, QuerySet
|
||||||
from django.utils.timezone import now as timezone_now
|
from django.utils.timezone import now as timezone_now
|
||||||
@@ -56,7 +56,7 @@ Usage examples:
|
|||||||
print("%25s %15d" % (count[1], count[0]))
|
print("%25s %15d" % (count[1], count[0]))
|
||||||
print("Total:", total)
|
print("Total:", total)
|
||||||
|
|
||||||
def handle(self, *args: Any, **options: Optional[str]) -> None:
|
def handle(self, *args: Any, **options: str) -> None:
|
||||||
realm = self.get_realm(options)
|
realm = self.get_realm(options)
|
||||||
if options["user"] is None:
|
if options["user"] is None:
|
||||||
if options["target"] == "server" and realm is None:
|
if options["target"] == "server" and realm is None:
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from datetime import timedelta
|
|
||||||
|
from datetime import datetime, timedelta
|
||||||
from typing import Any, Dict, List, Mapping, Optional, Type
|
from typing import Any, Dict, List, Mapping, Optional, Type
|
||||||
import mock
|
|
||||||
|
|
||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
from django.utils.timezone import now as timezone_now
|
from django.utils.timezone import now as timezone_now
|
||||||
@@ -12,10 +12,9 @@ from analytics.lib.time_utils import time_range
|
|||||||
from analytics.models import BaseCount, FillState, RealmCount, UserCount, \
|
from analytics.models import BaseCount, FillState, RealmCount, UserCount, \
|
||||||
StreamCount, InstallationCount
|
StreamCount, InstallationCount
|
||||||
from zerver.lib.actions import do_change_is_admin, STREAM_ASSIGNMENT_COLORS
|
from zerver.lib.actions import do_change_is_admin, STREAM_ASSIGNMENT_COLORS
|
||||||
from zerver.lib.create_user import create_user
|
|
||||||
from zerver.lib.timestamp import floor_to_day
|
from zerver.lib.timestamp import floor_to_day
|
||||||
from zerver.models import Realm, Stream, Client, \
|
from zerver.models import Realm, UserProfile, Stream, Client, \
|
||||||
Recipient, Subscription
|
RealmAuditLog, Recipient, Subscription
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
help = """Populates analytics tables with randomly generated data."""
|
help = """Populates analytics tables with randomly generated data."""
|
||||||
@@ -23,6 +22,20 @@ class Command(BaseCommand):
|
|||||||
DAYS_OF_DATA = 100
|
DAYS_OF_DATA = 100
|
||||||
random_seed = 26
|
random_seed = 26
|
||||||
|
|
||||||
|
def create_user(self, email: str,
|
||||||
|
full_name: str,
|
||||||
|
is_staff: bool,
|
||||||
|
date_joined: datetime,
|
||||||
|
realm: Realm) -> UserProfile:
|
||||||
|
user = UserProfile.objects.create(
|
||||||
|
email=email, full_name=full_name, is_staff=is_staff,
|
||||||
|
realm=realm, short_name=full_name, pointer=-1, last_pointer_updater='none',
|
||||||
|
api_key='42', date_joined=date_joined)
|
||||||
|
RealmAuditLog.objects.create(
|
||||||
|
realm=realm, modified_user=user, event_type=RealmAuditLog.USER_CREATED,
|
||||||
|
event_time=user.date_joined)
|
||||||
|
return user
|
||||||
|
|
||||||
def generate_fixture_data(self, stat: CountStat, business_hours_base: float,
|
def generate_fixture_data(self, stat: CountStat, business_hours_base: float,
|
||||||
non_business_hours_base: float, growth: float,
|
non_business_hours_base: float, growth: float,
|
||||||
autocorrelation: float, spikiness: float,
|
autocorrelation: float, spikiness: float,
|
||||||
@@ -55,16 +68,11 @@ class Command(BaseCommand):
|
|||||||
last_end_time = floor_to_day(timezone_now())
|
last_end_time = floor_to_day(timezone_now())
|
||||||
realm = Realm.objects.create(
|
realm = Realm.objects.create(
|
||||||
string_id='analytics', name='Analytics', date_created=installation_time)
|
string_id='analytics', name='Analytics', date_created=installation_time)
|
||||||
with mock.patch("zerver.lib.create_user.timezone_now", return_value=installation_time):
|
shylock = self.create_user('shylock@analytics.ds', 'Shylock', True, installation_time, realm)
|
||||||
shylock = create_user('shylock@analytics.ds', 'Shylock', realm,
|
|
||||||
full_name='Shylock', short_name='shylock',
|
|
||||||
is_realm_admin=True)
|
|
||||||
do_change_is_admin(shylock, True)
|
do_change_is_admin(shylock, True)
|
||||||
stream = Stream.objects.create(
|
stream = Stream.objects.create(
|
||||||
name='all', realm=realm, date_created=installation_time)
|
name='all', realm=realm, date_created=installation_time)
|
||||||
recipient = Recipient.objects.create(type_id=stream.id, type=Recipient.STREAM)
|
recipient = Recipient.objects.create(type_id=stream.id, type=Recipient.STREAM)
|
||||||
stream.recipient = recipient
|
|
||||||
stream.save(update_fields=["recipient"])
|
|
||||||
|
|
||||||
# Subscribe shylock to the stream to avoid invariant failures.
|
# Subscribe shylock to the stream to avoid invariant failures.
|
||||||
# TODO: This should use subscribe_users_to_streams from populate_db.
|
# TODO: This should use subscribe_users_to_streams from populate_db.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import datetime
|
|||||||
from argparse import ArgumentParser
|
from argparse import ArgumentParser
|
||||||
from typing import Any, List
|
from typing import Any, List
|
||||||
|
|
||||||
from django.core.management.base import BaseCommand, CommandError
|
from django.core.management.base import BaseCommand
|
||||||
from django.db.models import Count
|
from django.db.models import Count
|
||||||
from django.utils.timezone import now as timezone_now
|
from django.utils.timezone import now as timezone_now
|
||||||
|
|
||||||
@@ -33,32 +33,32 @@ class Command(BaseCommand):
|
|||||||
|
|
||||||
def messages_sent_by(self, user: UserProfile, days_ago: int) -> int:
|
def messages_sent_by(self, user: UserProfile, days_ago: int) -> int:
|
||||||
sent_time_cutoff = timezone_now() - datetime.timedelta(days=days_ago)
|
sent_time_cutoff = timezone_now() - datetime.timedelta(days=days_ago)
|
||||||
return human_messages.filter(sender=user, date_sent__gt=sent_time_cutoff).count()
|
return human_messages.filter(sender=user, pub_date__gt=sent_time_cutoff).count()
|
||||||
|
|
||||||
def total_messages(self, realm: Realm, days_ago: int) -> int:
|
def total_messages(self, realm: Realm, days_ago: int) -> int:
|
||||||
sent_time_cutoff = timezone_now() - datetime.timedelta(days=days_ago)
|
sent_time_cutoff = timezone_now() - datetime.timedelta(days=days_ago)
|
||||||
return Message.objects.filter(sender__realm=realm, date_sent__gt=sent_time_cutoff).count()
|
return Message.objects.filter(sender__realm=realm, pub_date__gt=sent_time_cutoff).count()
|
||||||
|
|
||||||
def human_messages(self, realm: Realm, days_ago: int) -> int:
|
def human_messages(self, realm: Realm, days_ago: int) -> int:
|
||||||
sent_time_cutoff = timezone_now() - datetime.timedelta(days=days_ago)
|
sent_time_cutoff = timezone_now() - datetime.timedelta(days=days_ago)
|
||||||
return human_messages.filter(sender__realm=realm, date_sent__gt=sent_time_cutoff).count()
|
return human_messages.filter(sender__realm=realm, pub_date__gt=sent_time_cutoff).count()
|
||||||
|
|
||||||
def api_messages(self, realm: Realm, days_ago: int) -> int:
|
def api_messages(self, realm: Realm, days_ago: int) -> int:
|
||||||
return (self.total_messages(realm, days_ago) - self.human_messages(realm, days_ago))
|
return (self.total_messages(realm, days_ago) - self.human_messages(realm, days_ago))
|
||||||
|
|
||||||
def stream_messages(self, realm: Realm, days_ago: int) -> int:
|
def stream_messages(self, realm: Realm, days_ago: int) -> int:
|
||||||
sent_time_cutoff = timezone_now() - datetime.timedelta(days=days_ago)
|
sent_time_cutoff = timezone_now() - datetime.timedelta(days=days_ago)
|
||||||
return human_messages.filter(sender__realm=realm, date_sent__gt=sent_time_cutoff,
|
return human_messages.filter(sender__realm=realm, pub_date__gt=sent_time_cutoff,
|
||||||
recipient__type=Recipient.STREAM).count()
|
recipient__type=Recipient.STREAM).count()
|
||||||
|
|
||||||
def private_messages(self, realm: Realm, days_ago: int) -> int:
|
def private_messages(self, realm: Realm, days_ago: int) -> int:
|
||||||
sent_time_cutoff = timezone_now() - datetime.timedelta(days=days_ago)
|
sent_time_cutoff = timezone_now() - datetime.timedelta(days=days_ago)
|
||||||
return human_messages.filter(sender__realm=realm, date_sent__gt=sent_time_cutoff).exclude(
|
return human_messages.filter(sender__realm=realm, pub_date__gt=sent_time_cutoff).exclude(
|
||||||
recipient__type=Recipient.STREAM).exclude(recipient__type=Recipient.HUDDLE).count()
|
recipient__type=Recipient.STREAM).exclude(recipient__type=Recipient.HUDDLE).count()
|
||||||
|
|
||||||
def group_private_messages(self, realm: Realm, days_ago: int) -> int:
|
def group_private_messages(self, realm: Realm, days_ago: int) -> int:
|
||||||
sent_time_cutoff = timezone_now() - datetime.timedelta(days=days_ago)
|
sent_time_cutoff = timezone_now() - datetime.timedelta(days=days_ago)
|
||||||
return human_messages.filter(sender__realm=realm, date_sent__gt=sent_time_cutoff).exclude(
|
return human_messages.filter(sender__realm=realm, pub_date__gt=sent_time_cutoff).exclude(
|
||||||
recipient__type=Recipient.STREAM).exclude(recipient__type=Recipient.PERSONAL).count()
|
recipient__type=Recipient.STREAM).exclude(recipient__type=Recipient.PERSONAL).count()
|
||||||
|
|
||||||
def report_percentage(self, numerator: float, denominator: float, text: str) -> None:
|
def report_percentage(self, numerator: float, denominator: float, text: str) -> None:
|
||||||
@@ -73,7 +73,8 @@ class Command(BaseCommand):
|
|||||||
try:
|
try:
|
||||||
realms = [get_realm(string_id) for string_id in options['realms']]
|
realms = [get_realm(string_id) for string_id in options['realms']]
|
||||||
except Realm.DoesNotExist as e:
|
except Realm.DoesNotExist as e:
|
||||||
raise CommandError(e)
|
print(e)
|
||||||
|
exit(1)
|
||||||
else:
|
else:
|
||||||
realms = Realm.objects.all()
|
realms = Realm.objects.all()
|
||||||
|
|
||||||
@@ -130,7 +131,7 @@ class Command(BaseCommand):
|
|||||||
user_profile__in=user_profiles, active=True)
|
user_profile__in=user_profiles, active=True)
|
||||||
|
|
||||||
# Streams not in home view
|
# Streams not in home view
|
||||||
non_home_view = active_user_subs.filter(is_muted=True).values(
|
non_home_view = active_user_subs.filter(in_home_view=False).values(
|
||||||
"user_profile").annotate(count=Count("user_profile"))
|
"user_profile").annotate(count=Count("user_profile"))
|
||||||
print("%d users have %d streams not in home view" % (
|
print("%d users have %d streams not in home view" % (
|
||||||
len(non_home_view), sum([elt["count"] for elt in non_home_view])))
|
len(non_home_view), sum([elt["count"] for elt in non_home_view])))
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
from argparse import ArgumentParser
|
from argparse import ArgumentParser
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from django.core.management.base import BaseCommand, CommandError
|
from django.core.management.base import BaseCommand
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
|
|
||||||
from zerver.models import Message, Realm, \
|
from zerver.models import Message, Realm, \
|
||||||
@@ -19,7 +19,8 @@ class Command(BaseCommand):
|
|||||||
try:
|
try:
|
||||||
realms = [get_realm(string_id) for string_id in options['realms']]
|
realms = [get_realm(string_id) for string_id in options['realms']]
|
||||||
except Realm.DoesNotExist as e:
|
except Realm.DoesNotExist as e:
|
||||||
raise CommandError(e)
|
print(e)
|
||||||
|
exit(1)
|
||||||
else:
|
else:
|
||||||
realms = Realm.objects.all()
|
realms = Realm.objects.all()
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import datetime
|
|||||||
from argparse import ArgumentParser
|
from argparse import ArgumentParser
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from django.core.management.base import BaseCommand, CommandError
|
from django.core.management.base import BaseCommand
|
||||||
from django.utils.timezone import now as timezone_now
|
from django.utils.timezone import now as timezone_now
|
||||||
|
|
||||||
from zerver.models import Message, Realm, Stream, UserProfile, get_realm
|
from zerver.models import Message, Realm, Stream, UserProfile, get_realm
|
||||||
@@ -17,14 +17,15 @@ class Command(BaseCommand):
|
|||||||
def messages_sent_by(self, user: UserProfile, week: int) -> int:
|
def messages_sent_by(self, user: UserProfile, week: int) -> int:
|
||||||
start = timezone_now() - datetime.timedelta(days=(week + 1)*7)
|
start = timezone_now() - datetime.timedelta(days=(week + 1)*7)
|
||||||
end = timezone_now() - datetime.timedelta(days=week*7)
|
end = timezone_now() - datetime.timedelta(days=week*7)
|
||||||
return Message.objects.filter(sender=user, date_sent__gt=start, date_sent__lte=end).count()
|
return Message.objects.filter(sender=user, pub_date__gt=start, pub_date__lte=end).count()
|
||||||
|
|
||||||
def handle(self, *args: Any, **options: Any) -> None:
|
def handle(self, *args: Any, **options: Any) -> None:
|
||||||
if options['realms']:
|
if options['realms']:
|
||||||
try:
|
try:
|
||||||
realms = [get_realm(string_id) for string_id in options['realms']]
|
realms = [get_realm(string_id) for string_id in options['realms']]
|
||||||
except Realm.DoesNotExist as e:
|
except Realm.DoesNotExist as e:
|
||||||
raise CommandError(e)
|
print(e)
|
||||||
|
exit(1)
|
||||||
else:
|
else:
|
||||||
realms = Realm.objects.all()
|
realms = Realm.objects.all()
|
||||||
|
|
||||||
@@ -37,5 +38,5 @@ class Command(BaseCommand):
|
|||||||
for user_profile in user_profiles:
|
for user_profile in user_profiles:
|
||||||
print("%35s" % (user_profile.email,), end=' ')
|
print("%35s" % (user_profile.email,), end=' ')
|
||||||
for week in range(10):
|
for week in range(10):
|
||||||
print("%5d" % (self.messages_sent_by(user_profile, week),), end=' ')
|
print("%5d" % (self.messages_sent_by(user_profile, week)), end=' ')
|
||||||
print("")
|
print("")
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from typing import Any, Dict, List, Optional, Tuple, Type
|
from typing import Any, Dict, List, Optional, Tuple, Type
|
||||||
|
|
||||||
import mock
|
|
||||||
import ujson
|
import ujson
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
from django.db import models
|
from django.db import models
|
||||||
@@ -22,7 +22,6 @@ from zerver.lib.actions import do_activate_user, do_create_user, \
|
|||||||
do_deactivate_user, do_reactivate_user, update_user_activity_interval, \
|
do_deactivate_user, do_reactivate_user, update_user_activity_interval, \
|
||||||
do_invite_users, do_revoke_user_invite, do_resend_user_invite_email, \
|
do_invite_users, do_revoke_user_invite, do_resend_user_invite_email, \
|
||||||
InvitationError
|
InvitationError
|
||||||
from zerver.lib.create_user import create_user
|
|
||||||
from zerver.lib.timestamp import TimezoneNotUTCException, floor_to_day
|
from zerver.lib.timestamp import TimezoneNotUTCException, floor_to_day
|
||||||
from zerver.lib.topic import DB_TOPIC_NAME
|
from zerver.lib.topic import DB_TOPIC_NAME
|
||||||
from zerver.models import Client, Huddle, Message, Realm, \
|
from zerver.models import Client, Huddle, Message, Realm, \
|
||||||
@@ -37,7 +36,6 @@ class AnalyticsTestCase(TestCase):
|
|||||||
TIME_LAST_HOUR = TIME_ZERO - HOUR
|
TIME_LAST_HOUR = TIME_ZERO - HOUR
|
||||||
|
|
||||||
def setUp(self) -> None:
|
def setUp(self) -> None:
|
||||||
super().setUp()
|
|
||||||
self.default_realm = Realm.objects.create(
|
self.default_realm = Realm.objects.create(
|
||||||
string_id='realmtest', name='Realm Test', date_created=self.TIME_ZERO - 2*self.DAY)
|
string_id='realmtest', name='Realm Test', date_created=self.TIME_ZERO - 2*self.DAY)
|
||||||
# used to generate unique names in self.create_*
|
# used to generate unique names in self.create_*
|
||||||
@@ -53,21 +51,16 @@ class AnalyticsTestCase(TestCase):
|
|||||||
'date_joined': self.TIME_LAST_HOUR,
|
'date_joined': self.TIME_LAST_HOUR,
|
||||||
'full_name': 'full_name',
|
'full_name': 'full_name',
|
||||||
'short_name': 'short_name',
|
'short_name': 'short_name',
|
||||||
'is_active': True,
|
'pointer': -1,
|
||||||
'is_bot': False,
|
'last_pointer_updater': 'seems unused?',
|
||||||
'realm': self.default_realm}
|
'realm': self.default_realm,
|
||||||
|
'api_key': '42'}
|
||||||
for key, value in defaults.items():
|
for key, value in defaults.items():
|
||||||
kwargs[key] = kwargs.get(key, value)
|
kwargs[key] = kwargs.get(key, value)
|
||||||
kwargs['delivery_email'] = kwargs['email']
|
kwargs['delivery_email'] = kwargs['email']
|
||||||
with mock.patch("zerver.lib.create_user.timezone_now", return_value=kwargs['date_joined']):
|
user_profile = UserProfile.objects.create(**kwargs)
|
||||||
pass_kwargs = {} # type: Dict[str, Any]
|
# TODO: Make this pass user_profile.full_clean()
|
||||||
if kwargs['is_bot']:
|
return user_profile
|
||||||
pass_kwargs['bot_type'] = UserProfile.DEFAULT_BOT
|
|
||||||
pass_kwargs['bot_owner'] = None
|
|
||||||
return create_user(kwargs['email'], 'password', kwargs['realm'],
|
|
||||||
active=kwargs['is_active'],
|
|
||||||
full_name=kwargs['full_name'], short_name=kwargs['short_name'],
|
|
||||||
is_realm_admin=True, **pass_kwargs)
|
|
||||||
|
|
||||||
def create_stream_with_recipient(self, **kwargs: Any) -> Tuple[Stream, Recipient]:
|
def create_stream_with_recipient(self, **kwargs: Any) -> Tuple[Stream, Recipient]:
|
||||||
self.name_counter += 1
|
self.name_counter += 1
|
||||||
@@ -95,7 +88,7 @@ class AnalyticsTestCase(TestCase):
|
|||||||
'recipient': recipient,
|
'recipient': recipient,
|
||||||
DB_TOPIC_NAME: 'subject',
|
DB_TOPIC_NAME: 'subject',
|
||||||
'content': 'hi',
|
'content': 'hi',
|
||||||
'date_sent': self.TIME_LAST_HOUR,
|
'pub_date': self.TIME_LAST_HOUR,
|
||||||
'sending_client': get_client("website")}
|
'sending_client': get_client("website")}
|
||||||
for key, value in defaults.items():
|
for key, value in defaults.items():
|
||||||
kwargs[key] = kwargs.get(key, value)
|
kwargs[key] = kwargs.get(key, value)
|
||||||
@@ -325,7 +318,7 @@ class TestCountStats(AnalyticsTestCase):
|
|||||||
recipient = self.create_stream_with_recipient(
|
recipient = self.create_stream_with_recipient(
|
||||||
name='stream %s' % (minutes_ago,), realm=self.second_realm,
|
name='stream %s' % (minutes_ago,), realm=self.second_realm,
|
||||||
date_created=creation_time)[1]
|
date_created=creation_time)[1]
|
||||||
self.create_message(user, recipient, date_sent=creation_time)
|
self.create_message(user, recipient, pub_date=creation_time)
|
||||||
self.hourly_user = get_user('user-1@second.analytics', self.second_realm)
|
self.hourly_user = get_user('user-1@second.analytics', self.second_realm)
|
||||||
self.daily_user = get_user('user-61@second.analytics', self.second_realm)
|
self.daily_user = get_user('user-61@second.analytics', self.second_realm)
|
||||||
|
|
||||||
@@ -370,8 +363,8 @@ class TestCountStats(AnalyticsTestCase):
|
|||||||
bot = self.create_user(is_bot=True)
|
bot = self.create_user(is_bot=True)
|
||||||
human1 = self.create_user()
|
human1 = self.create_user()
|
||||||
human2 = self.create_user()
|
human2 = self.create_user()
|
||||||
recipient_human1 = Recipient.objects.get(type_id=human1.id,
|
recipient_human1 = Recipient.objects.create(type_id=human1.id,
|
||||||
type=Recipient.PERSONAL)
|
type=Recipient.PERSONAL)
|
||||||
|
|
||||||
recipient_stream = self.create_stream_with_recipient()[1]
|
recipient_stream = self.create_stream_with_recipient()[1]
|
||||||
recipient_huddle = self.create_huddle_with_recipient()[1]
|
recipient_huddle = self.create_huddle_with_recipient()[1]
|
||||||
@@ -425,9 +418,9 @@ class TestCountStats(AnalyticsTestCase):
|
|||||||
self.create_message(user2, recipient_huddle2)
|
self.create_message(user2, recipient_huddle2)
|
||||||
|
|
||||||
# private messages
|
# private messages
|
||||||
recipient_user1 = Recipient.objects.get(type_id=user1.id, type=Recipient.PERSONAL)
|
recipient_user1 = Recipient.objects.create(type_id=user1.id, type=Recipient.PERSONAL)
|
||||||
recipient_user2 = Recipient.objects.get(type_id=user2.id, type=Recipient.PERSONAL)
|
recipient_user2 = Recipient.objects.create(type_id=user2.id, type=Recipient.PERSONAL)
|
||||||
recipient_user3 = Recipient.objects.get(type_id=user3.id, type=Recipient.PERSONAL)
|
recipient_user3 = Recipient.objects.create(type_id=user3.id, type=Recipient.PERSONAL)
|
||||||
self.create_message(user1, recipient_user2)
|
self.create_message(user1, recipient_user2)
|
||||||
self.create_message(user2, recipient_user1)
|
self.create_message(user2, recipient_user1)
|
||||||
self.create_message(user3, recipient_user3)
|
self.create_message(user3, recipient_user3)
|
||||||
@@ -459,7 +452,7 @@ class TestCountStats(AnalyticsTestCase):
|
|||||||
self.current_property = stat.property
|
self.current_property = stat.property
|
||||||
|
|
||||||
user = self.create_user(id=1000)
|
user = self.create_user(id=1000)
|
||||||
user_recipient = Recipient.objects.get(type_id=user.id, type=Recipient.PERSONAL)
|
user_recipient = Recipient.objects.create(type_id=user.id, type=Recipient.PERSONAL)
|
||||||
stream_recipient = self.create_stream_with_recipient(id=1000)[1]
|
stream_recipient = self.create_stream_with_recipient(id=1000)[1]
|
||||||
huddle_recipient = self.create_huddle_with_recipient(id=1000)[1]
|
huddle_recipient = self.create_huddle_with_recipient(id=1000)[1]
|
||||||
|
|
||||||
@@ -479,7 +472,7 @@ class TestCountStats(AnalyticsTestCase):
|
|||||||
|
|
||||||
user1 = self.create_user(is_bot=True)
|
user1 = self.create_user(is_bot=True)
|
||||||
user2 = self.create_user()
|
user2 = self.create_user()
|
||||||
recipient_user2 = Recipient.objects.get(type_id=user2.id, type=Recipient.PERSONAL)
|
recipient_user2 = Recipient.objects.create(type_id=user2.id, type=Recipient.PERSONAL)
|
||||||
|
|
||||||
recipient_stream = self.create_stream_with_recipient()[1]
|
recipient_stream = self.create_stream_with_recipient()[1]
|
||||||
recipient_huddle = self.create_huddle_with_recipient()[1]
|
recipient_huddle = self.create_huddle_with_recipient()[1]
|
||||||
@@ -515,7 +508,7 @@ class TestCountStats(AnalyticsTestCase):
|
|||||||
bot = self.create_user(is_bot=True)
|
bot = self.create_user(is_bot=True)
|
||||||
human1 = self.create_user()
|
human1 = self.create_user()
|
||||||
human2 = self.create_user()
|
human2 = self.create_user()
|
||||||
recipient_human1 = Recipient.objects.get(type_id=human1.id, type=Recipient.PERSONAL)
|
recipient_human1 = Recipient.objects.create(type_id=human1.id, type=Recipient.PERSONAL)
|
||||||
|
|
||||||
stream1, recipient_stream1 = self.create_stream_with_recipient()
|
stream1, recipient_stream1 = self.create_stream_with_recipient()
|
||||||
stream2, recipient_stream2 = self.create_stream_with_recipient()
|
stream2, recipient_stream2 = self.create_stream_with_recipient()
|
||||||
@@ -892,7 +885,7 @@ class TestActiveUsersAudit(AnalyticsTestCase):
|
|||||||
self.stat = COUNT_STATS['active_users_audit:is_bot:day']
|
self.stat = COUNT_STATS['active_users_audit:is_bot:day']
|
||||||
self.current_property = self.stat.property
|
self.current_property = self.stat.property
|
||||||
|
|
||||||
def add_event(self, event_type: int, days_offset: float,
|
def add_event(self, event_type: str, days_offset: float,
|
||||||
user: Optional[UserProfile]=None) -> None:
|
user: Optional[UserProfile]=None) -> None:
|
||||||
hours_offset = int(24*days_offset)
|
hours_offset = int(24*days_offset)
|
||||||
if user is None:
|
if user is None:
|
||||||
@@ -986,7 +979,7 @@ class TestActiveUsersAudit(AnalyticsTestCase):
|
|||||||
# doesn't go through do_create_user. Mainly just want to make sure that
|
# doesn't go through do_create_user. Mainly just want to make sure that
|
||||||
# that situation doesn't throw an error.
|
# that situation doesn't throw an error.
|
||||||
def test_empty_realm_or_user_with_no_relevant_activity(self) -> None:
|
def test_empty_realm_or_user_with_no_relevant_activity(self) -> None:
|
||||||
self.add_event(RealmAuditLog.USER_SOFT_ACTIVATED, 1)
|
self.add_event('unrelated', 1)
|
||||||
self.create_user() # also test a user with no RealmAuditLog entries
|
self.create_user() # also test a user with no RealmAuditLog entries
|
||||||
Realm.objects.create(string_id='moo', name='moo')
|
Realm.objects.create(string_id='moo', name='moo')
|
||||||
do_fill_count_stat_at_hour(self.stat, self.TIME_ZERO)
|
do_fill_count_stat_at_hour(self.stat, self.TIME_ZERO)
|
||||||
@@ -994,14 +987,14 @@ class TestActiveUsersAudit(AnalyticsTestCase):
|
|||||||
|
|
||||||
def test_max_audit_entry_is_unrelated(self) -> None:
|
def test_max_audit_entry_is_unrelated(self) -> None:
|
||||||
self.add_event(RealmAuditLog.USER_CREATED, 1)
|
self.add_event(RealmAuditLog.USER_CREATED, 1)
|
||||||
self.add_event(RealmAuditLog.USER_SOFT_ACTIVATED, .5)
|
self.add_event('unrelated', .5)
|
||||||
do_fill_count_stat_at_hour(self.stat, self.TIME_ZERO)
|
do_fill_count_stat_at_hour(self.stat, self.TIME_ZERO)
|
||||||
self.assertTableState(UserCount, ['subgroup'], [['false']])
|
self.assertTableState(UserCount, ['subgroup'], [['false']])
|
||||||
|
|
||||||
# Simultaneous related audit entries should not be allowed, and so not testing for that.
|
# Simultaneous related audit entries should not be allowed, and so not testing for that.
|
||||||
def test_simultaneous_unrelated_audit_entry(self) -> None:
|
def test_simultaneous_unrelated_audit_entry(self) -> None:
|
||||||
self.add_event(RealmAuditLog.USER_CREATED, 1)
|
self.add_event(RealmAuditLog.USER_CREATED, 1)
|
||||||
self.add_event(RealmAuditLog.USER_SOFT_ACTIVATED, 1)
|
self.add_event('unrelated', 1)
|
||||||
do_fill_count_stat_at_hour(self.stat, self.TIME_ZERO)
|
do_fill_count_stat_at_hour(self.stat, self.TIME_ZERO)
|
||||||
self.assertTableState(UserCount, ['subgroup'], [['false']])
|
self.assertTableState(UserCount, ['subgroup'], [['false']])
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ from typing import List, Optional
|
|||||||
|
|
||||||
import mock
|
import mock
|
||||||
from django.utils.timezone import utc
|
from django.utils.timezone import utc
|
||||||
from django.http import HttpResponse
|
|
||||||
import ujson
|
|
||||||
|
|
||||||
from analytics.lib.counts import COUNT_STATS, CountStat
|
from analytics.lib.counts import COUNT_STATS, CountStat
|
||||||
from analytics.lib.time_utils import time_range
|
from analytics.lib.time_utils import time_range
|
||||||
@@ -15,9 +13,7 @@ from analytics.views import rewrite_client_arrays, \
|
|||||||
from zerver.lib.test_classes import ZulipTestCase
|
from zerver.lib.test_classes import ZulipTestCase
|
||||||
from zerver.lib.timestamp import ceiling_to_day, \
|
from zerver.lib.timestamp import ceiling_to_day, \
|
||||||
ceiling_to_hour, datetime_to_timestamp
|
ceiling_to_hour, datetime_to_timestamp
|
||||||
from zerver.lib.actions import do_create_multiuse_invite_link, \
|
from zerver.models import Client, get_realm
|
||||||
do_send_realm_reactivation_email
|
|
||||||
from zerver.models import Client, get_realm, MultiuseInvite
|
|
||||||
|
|
||||||
class TestStatsEndpoint(ZulipTestCase):
|
class TestStatsEndpoint(ZulipTestCase):
|
||||||
def test_stats(self) -> None:
|
def test_stats(self) -> None:
|
||||||
@@ -72,7 +68,6 @@ class TestStatsEndpoint(ZulipTestCase):
|
|||||||
|
|
||||||
class TestGetChartData(ZulipTestCase):
|
class TestGetChartData(ZulipTestCase):
|
||||||
def setUp(self) -> None:
|
def setUp(self) -> None:
|
||||||
super().setUp()
|
|
||||||
self.realm = get_realm('zulip')
|
self.realm = get_realm('zulip')
|
||||||
self.user = self.example_user('hamlet')
|
self.user = self.example_user('hamlet')
|
||||||
self.login(self.user.email)
|
self.login(self.user.email)
|
||||||
@@ -332,215 +327,6 @@ class TestGetChartData(ZulipTestCase):
|
|||||||
{'chart_name': 'number_of_humans'})
|
{'chart_name': 'number_of_humans'})
|
||||||
self.assert_json_success(result)
|
self.assert_json_success(result)
|
||||||
|
|
||||||
class TestSupportEndpoint(ZulipTestCase):
|
|
||||||
def test_search(self) -> None:
|
|
||||||
def check_hamlet_user_query_result(result: HttpResponse) -> None:
|
|
||||||
self.assert_in_success_response(['<span class="label">user</span>\n', '<h3>King Hamlet</h3>',
|
|
||||||
'<b>Email</b>: hamlet@zulip.com', '<b>Is active</b>: True<br>',
|
|
||||||
'<b>Admins</b>: iago@zulip.com\n',
|
|
||||||
'class="copy-button" data-copytext="iago@zulip.com"'
|
|
||||||
], result)
|
|
||||||
|
|
||||||
def check_zulip_realm_query_result(result: HttpResponse) -> None:
|
|
||||||
zulip_realm = get_realm("zulip")
|
|
||||||
self.assert_in_success_response(['<input type="hidden" name="realm_id" value="%s"' % (zulip_realm.id,),
|
|
||||||
'Zulip Dev</h3>',
|
|
||||||
'<option value="1" selected>Self Hosted</option>',
|
|
||||||
'<option value="2" >Limited</option>',
|
|
||||||
'input type="number" name="discount" value="None"',
|
|
||||||
'<option value="active" selected>Active</option>',
|
|
||||||
'<option value="deactivated" >Deactivated</option>',
|
|
||||||
'scrub-realm-button">',
|
|
||||||
'data-string-id="zulip"'], result)
|
|
||||||
|
|
||||||
def check_lear_realm_query_result(result: HttpResponse) -> None:
|
|
||||||
lear_realm = get_realm("lear")
|
|
||||||
self.assert_in_success_response(['<input type="hidden" name="realm_id" value="%s"' % (lear_realm.id,),
|
|
||||||
'Lear & Co.</h3>',
|
|
||||||
'<option value="1" selected>Self Hosted</option>',
|
|
||||||
'<option value="2" >Limited</option>',
|
|
||||||
'input type="number" name="discount" value="None"',
|
|
||||||
'<option value="active" selected>Active</option>',
|
|
||||||
'<option value="deactivated" >Deactivated</option>',
|
|
||||||
'scrub-realm-button">',
|
|
||||||
'data-string-id="lear"'], result)
|
|
||||||
|
|
||||||
def check_preregistration_user_query_result(result: HttpResponse, email: str, invite: Optional[bool]=False) -> None:
|
|
||||||
self.assert_in_success_response(['<span class="label">preregistration user</span>\n',
|
|
||||||
'<b>Email</b>: {}'.format(email),
|
|
||||||
], result)
|
|
||||||
if invite:
|
|
||||||
self.assert_in_success_response(['<span class="label">invite</span>'], result)
|
|
||||||
self.assert_in_success_response(['<b>Expires in</b>: 1\xa0week, 3',
|
|
||||||
'<b>Status</b>: Link has never been clicked'], result)
|
|
||||||
self.assert_in_success_response([], result)
|
|
||||||
else:
|
|
||||||
self.assert_not_in_success_response(['<span class="label">invite</span>'], result)
|
|
||||||
self.assert_in_success_response(['<b>Expires in</b>: 1\xa0day',
|
|
||||||
'<b>Status</b>: Link has never been clicked'], result)
|
|
||||||
|
|
||||||
def check_realm_creation_query_result(result: HttpResponse, email: str) -> None:
|
|
||||||
self.assert_in_success_response(['<span class="label">preregistration user</span>\n',
|
|
||||||
'<span class="label">realm creation</span>\n',
|
|
||||||
'<b>Link</b>: http://zulip.testserver/accounts/do_confirm/',
|
|
||||||
'<b>Expires in</b>: 1\xa0day<br>\n'
|
|
||||||
], result)
|
|
||||||
|
|
||||||
def check_multiuse_invite_link_query_result(result: HttpResponse) -> None:
|
|
||||||
self.assert_in_success_response(['<span class="label">multiuse invite</span>\n',
|
|
||||||
'<b>Link</b>: http://zulip.testserver/join/',
|
|
||||||
'<b>Expires in</b>: 1\xa0week, 3'
|
|
||||||
], result)
|
|
||||||
|
|
||||||
def check_realm_reactivation_link_query_result(result: HttpResponse) -> None:
|
|
||||||
self.assert_in_success_response(['<span class="label">realm reactivation</span>\n',
|
|
||||||
'<b>Link</b>: http://zulip.testserver/reactivate/',
|
|
||||||
'<b>Expires in</b>: 1\xa0day'
|
|
||||||
], result)
|
|
||||||
|
|
||||||
cordelia_email = self.example_email("cordelia")
|
|
||||||
self.login(cordelia_email)
|
|
||||||
|
|
||||||
result = self.client_get("/activity/support")
|
|
||||||
self.assertEqual(result.status_code, 302)
|
|
||||||
self.assertEqual(result["Location"], "/login/")
|
|
||||||
|
|
||||||
iago_email = self.example_email("iago")
|
|
||||||
self.login(iago_email)
|
|
||||||
|
|
||||||
result = self.client_get("/activity/support")
|
|
||||||
self.assert_in_success_response(['<input type="text" name="q" class="input-xxlarge search-query"'], result)
|
|
||||||
|
|
||||||
result = self.client_get("/activity/support", {"q": "hamlet@zulip.com"})
|
|
||||||
check_hamlet_user_query_result(result)
|
|
||||||
check_zulip_realm_query_result(result)
|
|
||||||
|
|
||||||
result = self.client_get("/activity/support", {"q": "lear"})
|
|
||||||
check_lear_realm_query_result(result)
|
|
||||||
|
|
||||||
result = self.client_get("/activity/support", {"q": "http://lear.testserver"})
|
|
||||||
check_lear_realm_query_result(result)
|
|
||||||
|
|
||||||
with self.settings(REALM_HOSTS={'zulip': 'localhost'}):
|
|
||||||
result = self.client_get("/activity/support", {"q": "http://localhost"})
|
|
||||||
check_zulip_realm_query_result(result)
|
|
||||||
|
|
||||||
result = self.client_get("/activity/support", {"q": "hamlet@zulip.com, lear"})
|
|
||||||
check_hamlet_user_query_result(result)
|
|
||||||
check_zulip_realm_query_result(result)
|
|
||||||
check_lear_realm_query_result(result)
|
|
||||||
|
|
||||||
result = self.client_get("/activity/support", {"q": "lear, Hamlet <hamlet@zulip.com>"})
|
|
||||||
check_hamlet_user_query_result(result)
|
|
||||||
check_zulip_realm_query_result(result)
|
|
||||||
check_lear_realm_query_result(result)
|
|
||||||
|
|
||||||
self.client_post('/accounts/home/', {'email': self.nonreg_email("test")})
|
|
||||||
self.login(iago_email)
|
|
||||||
result = self.client_get("/activity/support", {"q": self.nonreg_email("test")})
|
|
||||||
check_preregistration_user_query_result(result, self.nonreg_email("test"))
|
|
||||||
check_zulip_realm_query_result(result)
|
|
||||||
|
|
||||||
stream_ids = [self.get_stream_id("Denmark")]
|
|
||||||
invitee_emails = [self.nonreg_email("test1")]
|
|
||||||
self.client_post("/json/invites", {"invitee_emails": invitee_emails,
|
|
||||||
"stream_ids": ujson.dumps(stream_ids), "invite_as": 1})
|
|
||||||
result = self.client_get("/activity/support", {"q": self.nonreg_email("test1")})
|
|
||||||
check_preregistration_user_query_result(result, self.nonreg_email("test1"), invite=True)
|
|
||||||
check_zulip_realm_query_result(result)
|
|
||||||
|
|
||||||
email = self.nonreg_email('alice')
|
|
||||||
self.client_post('/new/', {'email': email})
|
|
||||||
result = self.client_get("/activity/support", {"q": email})
|
|
||||||
check_realm_creation_query_result(result, email)
|
|
||||||
|
|
||||||
do_create_multiuse_invite_link(self.example_user("hamlet"), invited_as=1)
|
|
||||||
result = self.client_get("/activity/support", {"q": "zulip"})
|
|
||||||
check_multiuse_invite_link_query_result(result)
|
|
||||||
check_zulip_realm_query_result(result)
|
|
||||||
MultiuseInvite.objects.all().delete()
|
|
||||||
|
|
||||||
do_send_realm_reactivation_email(get_realm("zulip"))
|
|
||||||
result = self.client_get("/activity/support", {"q": "zulip"})
|
|
||||||
check_realm_reactivation_link_query_result(result)
|
|
||||||
check_zulip_realm_query_result(result)
|
|
||||||
|
|
||||||
def test_change_plan_type(self) -> None:
|
|
||||||
cordelia = self.example_user("cordelia")
|
|
||||||
self.login(cordelia.email)
|
|
||||||
|
|
||||||
result = self.client_post("/activity/support", {"realm_id": "%s" % (cordelia.realm_id,), "plan_type": "2"})
|
|
||||||
self.assertEqual(result.status_code, 302)
|
|
||||||
self.assertEqual(result["Location"], "/login/")
|
|
||||||
|
|
||||||
iago = self.example_user("iago")
|
|
||||||
self.login(iago.email)
|
|
||||||
|
|
||||||
with mock.patch("analytics.views.do_change_plan_type") as m:
|
|
||||||
result = self.client_post("/activity/support", {"realm_id": "%s" % (iago.realm_id,), "plan_type": "2"})
|
|
||||||
m.assert_called_once_with(get_realm("zulip"), 2)
|
|
||||||
self.assert_in_success_response(["Plan type of Zulip Dev changed from self hosted to limited"], result)
|
|
||||||
|
|
||||||
def test_attach_discount(self) -> None:
|
|
||||||
lear_realm = get_realm("lear")
|
|
||||||
cordelia_email = self.example_email("cordelia")
|
|
||||||
self.login(cordelia_email)
|
|
||||||
|
|
||||||
result = self.client_post("/activity/support", {"realm_id": "%s" % (lear_realm.id,), "discount": "25"})
|
|
||||||
self.assertEqual(result.status_code, 302)
|
|
||||||
self.assertEqual(result["Location"], "/login/")
|
|
||||||
|
|
||||||
iago_email = self.example_email("iago")
|
|
||||||
self.login(iago_email)
|
|
||||||
|
|
||||||
with mock.patch("analytics.views.attach_discount_to_realm") as m:
|
|
||||||
result = self.client_post("/activity/support", {"realm_id": "%s" % (lear_realm.id,), "discount": "25"})
|
|
||||||
m.assert_called_once_with(get_realm("lear"), 25)
|
|
||||||
self.assert_in_success_response(["Discount of Lear & Co. changed to 25 from None"], result)
|
|
||||||
|
|
||||||
def test_activate_or_deactivate_realm(self) -> None:
|
|
||||||
lear_realm = get_realm("lear")
|
|
||||||
cordelia_email = self.example_email("cordelia")
|
|
||||||
self.login(cordelia_email)
|
|
||||||
|
|
||||||
result = self.client_post("/activity/support", {"realm_id": "%s" % (lear_realm.id,), "status": "deactivated"})
|
|
||||||
self.assertEqual(result.status_code, 302)
|
|
||||||
self.assertEqual(result["Location"], "/login/")
|
|
||||||
|
|
||||||
iago_email = self.example_email("iago")
|
|
||||||
self.login(iago_email)
|
|
||||||
|
|
||||||
with mock.patch("analytics.views.do_deactivate_realm") as m:
|
|
||||||
result = self.client_post("/activity/support", {"realm_id": "%s" % (lear_realm.id,), "status": "deactivated"})
|
|
||||||
m.assert_called_once_with(lear_realm, self.example_user("iago"))
|
|
||||||
self.assert_in_success_response(["Lear & Co. deactivated"], result)
|
|
||||||
|
|
||||||
with mock.patch("analytics.views.do_send_realm_reactivation_email") as m:
|
|
||||||
result = self.client_post("/activity/support", {"realm_id": "%s" % (lear_realm.id,), "status": "active"})
|
|
||||||
m.assert_called_once_with(lear_realm)
|
|
||||||
self.assert_in_success_response(["Realm reactivation email sent to admins of Lear"], result)
|
|
||||||
|
|
||||||
def test_scrub_realm(self) -> None:
|
|
||||||
lear_realm = get_realm("lear")
|
|
||||||
cordelia_email = self.example_email("cordelia")
|
|
||||||
self.login(cordelia_email)
|
|
||||||
|
|
||||||
result = self.client_post("/activity/support", {"realm_id": "%s" % (lear_realm.id,), "discount": "25"})
|
|
||||||
self.assertEqual(result.status_code, 302)
|
|
||||||
self.assertEqual(result["Location"], "/login/")
|
|
||||||
|
|
||||||
iago_email = self.example_email("iago")
|
|
||||||
self.login(iago_email)
|
|
||||||
|
|
||||||
with mock.patch("analytics.views.do_scrub_realm") as m:
|
|
||||||
result = self.client_post("/activity/support", {"realm_id": "%s" % (lear_realm.id,), "scrub_realm": "scrub_realm"})
|
|
||||||
m.assert_called_once_with(lear_realm)
|
|
||||||
self.assert_in_success_response(["Lear & Co. scrubbed"], result)
|
|
||||||
|
|
||||||
with mock.patch("analytics.views.do_scrub_realm") as m:
|
|
||||||
result = self.client_post("/activity/support", {"realm_id": "%s" % (lear_realm.id,)})
|
|
||||||
m.assert_not_called()
|
|
||||||
|
|
||||||
class TestGetChartDataHelpers(ZulipTestCase):
|
class TestGetChartDataHelpers(ZulipTestCase):
|
||||||
# last_successful_fill is in analytics/models.py, but get_chart_data is
|
# last_successful_fill is in analytics/models.py, but get_chart_data is
|
||||||
# the only function that uses it at the moment
|
# the only function that uses it at the moment
|
||||||
|
|||||||
@@ -7,8 +7,6 @@ i18n_urlpatterns = [
|
|||||||
# Server admin (user_profile.is_staff) visible stats pages
|
# Server admin (user_profile.is_staff) visible stats pages
|
||||||
url(r'^activity$', analytics.views.get_activity,
|
url(r'^activity$', analytics.views.get_activity,
|
||||||
name='analytics.views.get_activity'),
|
name='analytics.views.get_activity'),
|
||||||
url(r'^activity/support$', analytics.views.support,
|
|
||||||
name='analytics.views.support'),
|
|
||||||
url(r'^realm_activity/(?P<realm_str>[\S]+)/$', analytics.views.get_realm_activity,
|
url(r'^realm_activity/(?P<realm_str>[\S]+)/$', analytics.views.get_realm_activity,
|
||||||
name='analytics.views.get_realm_activity'),
|
name='analytics.views.get_realm_activity'),
|
||||||
url(r'^user_activity/(?P<email>[\S]+)/$', analytics.views.get_user_activity,
|
url(r'^user_activity/(?P<email>[\S]+)/$', analytics.views.get_user_activity,
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
|
|
||||||
import itertools
|
import itertools
|
||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
import time
|
import time
|
||||||
import urllib
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from decimal import Decimal
|
|
||||||
|
|
||||||
from typing import Any, Callable, Dict, List, \
|
from typing import Any, Callable, Dict, List, \
|
||||||
Optional, Set, Tuple, Type, Union
|
Optional, Set, Tuple, Type, Union, cast
|
||||||
|
|
||||||
import pytz
|
import pytz
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
@@ -20,34 +18,21 @@ from django.shortcuts import render
|
|||||||
from django.template import loader
|
from django.template import loader
|
||||||
from django.utils.timezone import now as timezone_now, utc as timezone_utc
|
from django.utils.timezone import now as timezone_now, utc as timezone_utc
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
from django.utils.timesince import timesince
|
|
||||||
from django.core.validators import URLValidator
|
|
||||||
from django.core.exceptions import ValidationError
|
|
||||||
from jinja2 import Markup as mark_safe
|
from jinja2 import Markup as mark_safe
|
||||||
|
|
||||||
from analytics.lib.counts import COUNT_STATS, CountStat
|
from analytics.lib.counts import COUNT_STATS, CountStat
|
||||||
from analytics.lib.time_utils import time_range
|
from analytics.lib.time_utils import time_range
|
||||||
from analytics.models import BaseCount, InstallationCount, \
|
from analytics.models import BaseCount, InstallationCount, \
|
||||||
RealmCount, StreamCount, UserCount, last_successful_fill, installation_epoch
|
RealmCount, StreamCount, UserCount, last_successful_fill, installation_epoch
|
||||||
from confirmation.models import Confirmation, confirmation_url, _properties
|
|
||||||
from zerver.decorator import require_server_admin, require_server_admin_api, \
|
from zerver.decorator import require_server_admin, require_server_admin_api, \
|
||||||
to_non_negative_int, to_utc_datetime, zulip_login_required, require_non_guest_user
|
to_non_negative_int, to_utc_datetime, zulip_login_required, require_non_guest_user
|
||||||
from zerver.lib.exceptions import JsonableError
|
from zerver.lib.exceptions import JsonableError
|
||||||
|
from zerver.lib.json_encoder_for_html import JSONEncoderForHTML
|
||||||
from zerver.lib.request import REQ, has_request_variables
|
from zerver.lib.request import REQ, has_request_variables
|
||||||
from zerver.lib.response import json_success
|
from zerver.lib.response import json_success
|
||||||
from zerver.lib.timestamp import convert_to_UTC, timestamp_to_datetime
|
from zerver.lib.timestamp import convert_to_UTC, timestamp_to_datetime
|
||||||
from zerver.lib.realm_icon import realm_icon_url
|
from zerver.models import Client, get_realm, Realm, \
|
||||||
from zerver.views.invite import get_invitee_emails_set
|
UserActivity, UserActivityInterval, UserProfile
|
||||||
from zerver.lib.subdomains import get_subdomain_from_hostname
|
|
||||||
from zerver.lib.actions import do_change_plan_type, do_deactivate_realm, \
|
|
||||||
do_send_realm_reactivation_email, do_scrub_realm
|
|
||||||
from confirmation.settings import STATUS_ACTIVE
|
|
||||||
|
|
||||||
if settings.BILLING_ENABLED:
|
|
||||||
from corporate.lib.stripe import attach_discount_to_realm, get_discount_for_realm
|
|
||||||
|
|
||||||
from zerver.models import Client, get_realm, Realm, UserActivity, UserActivityInterval, \
|
|
||||||
UserProfile, PreregistrationUser, MultiuseInvite
|
|
||||||
|
|
||||||
if settings.ZILENCER_ENABLED:
|
if settings.ZILENCER_ENABLED:
|
||||||
from zilencer.models import RemoteInstallationCount, RemoteRealmCount, \
|
from zilencer.models import RemoteInstallationCount, RemoteRealmCount, \
|
||||||
@@ -69,23 +54,22 @@ def render_stats(request: HttpRequest, data_url_suffix: str, target_name: str,
|
|||||||
return render(request,
|
return render(request,
|
||||||
'analytics/stats.html',
|
'analytics/stats.html',
|
||||||
context=dict(target_name=target_name,
|
context=dict(target_name=target_name,
|
||||||
page_params=page_params))
|
page_params=JSONEncoderForHTML().encode(page_params)))
|
||||||
|
|
||||||
@zulip_login_required
|
@zulip_login_required
|
||||||
def stats(request: HttpRequest) -> HttpResponse:
|
def stats(request: HttpRequest) -> HttpResponse:
|
||||||
realm = request.user.realm
|
realm = request.user.realm
|
||||||
if request.user.is_guest:
|
if request.user.is_guest:
|
||||||
# TODO: Make @zulip_login_required pass the UserProfile so we
|
# TODO: Make @zulip_login_required pass the UserProfile so we
|
||||||
# can use @require_member_or_admin
|
# can use @require_non_guest_human_user
|
||||||
raise JsonableError(_("Not allowed for guest users"))
|
raise JsonableError(_("Not allowed for guest users"))
|
||||||
return render_stats(request, '', realm.name or realm.string_id)
|
return render_stats(request, '', realm.name or realm.string_id)
|
||||||
|
|
||||||
@require_server_admin
|
@require_server_admin
|
||||||
@has_request_variables
|
@has_request_variables
|
||||||
def stats_for_realm(request: HttpRequest, realm_str: str) -> HttpResponse:
|
def stats_for_realm(request: HttpRequest, realm_str: str) -> HttpResponse:
|
||||||
try:
|
realm = get_realm(realm_str)
|
||||||
realm = get_realm(realm_str)
|
if realm is None:
|
||||||
except Realm.DoesNotExist:
|
|
||||||
return HttpResponseNotFound("Realm %s does not exist" % (realm_str,))
|
return HttpResponseNotFound("Realm %s does not exist" % (realm_str,))
|
||||||
|
|
||||||
return render_stats(request, '/realm/%s' % (realm_str,), realm.name or realm.string_id)
|
return render_stats(request, '/realm/%s' % (realm_str,), realm.name or realm.string_id)
|
||||||
@@ -102,9 +86,8 @@ def stats_for_remote_realm(request: HttpRequest, remote_server_id: str,
|
|||||||
@has_request_variables
|
@has_request_variables
|
||||||
def get_chart_data_for_realm(request: HttpRequest, user_profile: UserProfile,
|
def get_chart_data_for_realm(request: HttpRequest, user_profile: UserProfile,
|
||||||
realm_str: str, **kwargs: Any) -> HttpResponse:
|
realm_str: str, **kwargs: Any) -> HttpResponse:
|
||||||
try:
|
realm = get_realm(realm_str)
|
||||||
realm = get_realm(realm_str)
|
if realm is None:
|
||||||
except Realm.DoesNotExist:
|
|
||||||
raise JsonableError(_("Invalid organization"))
|
raise JsonableError(_("Invalid organization"))
|
||||||
|
|
||||||
return get_chart_data(request=request, user_profile=user_profile, realm=realm, **kwargs)
|
return get_chart_data(request=request, user_profile=user_profile, realm=realm, **kwargs)
|
||||||
@@ -126,7 +109,7 @@ def stats_for_installation(request: HttpRequest) -> HttpResponse:
|
|||||||
def stats_for_remote_installation(request: HttpRequest, remote_server_id: str) -> HttpResponse:
|
def stats_for_remote_installation(request: HttpRequest, remote_server_id: str) -> HttpResponse:
|
||||||
server = RemoteZulipServer.objects.get(id=remote_server_id)
|
server = RemoteZulipServer.objects.get(id=remote_server_id)
|
||||||
return render_stats(request, '/remote/%s/installation' % (server.id,),
|
return render_stats(request, '/remote/%s/installation' % (server.id,),
|
||||||
'remote Installation %s' % (server.hostname,), True, True)
|
'remote Installation %s' % (server.hostname), True, True)
|
||||||
|
|
||||||
@require_server_admin_api
|
@require_server_admin_api
|
||||||
@has_request_variables
|
@has_request_variables
|
||||||
@@ -244,7 +227,7 @@ def get_chart_data(request: HttpRequest, user_profile: UserProfile, chart_name:
|
|||||||
if end is None:
|
if end is None:
|
||||||
end = max(last_successful_fill(stat.property) or
|
end = max(last_successful_fill(stat.property) or
|
||||||
datetime.min.replace(tzinfo=timezone_utc) for stat in stats)
|
datetime.min.replace(tzinfo=timezone_utc) for stat in stats)
|
||||||
if start > end:
|
if end is None or start > end:
|
||||||
logging.warning("User from realm %s attempted to access /stats, but the computed "
|
logging.warning("User from realm %s attempted to access /stats, but the computed "
|
||||||
"start time: %s (creation of realm or installation) is later than the computed "
|
"start time: %s (creation of realm or installation) is later than the computed "
|
||||||
"end time: %s (last successful analytics update). Is the "
|
"end time: %s (last successful analytics update). Is the "
|
||||||
@@ -266,7 +249,7 @@ def get_chart_data(request: HttpRequest, user_profile: UserProfile, chart_name:
|
|||||||
id_value = {
|
id_value = {
|
||||||
InstallationCount: -1,
|
InstallationCount: -1,
|
||||||
RealmCount: realm.id,
|
RealmCount: realm.id,
|
||||||
RemoteInstallationCount: server.id if server is not None else None,
|
RemoteInstallationCount: cast(RemoteZulipServer, server).id if server is not None else None,
|
||||||
# TODO: RemoteRealmCount logic doesn't correctly handle
|
# TODO: RemoteRealmCount logic doesn't correctly handle
|
||||||
# filtering by server_id as well.
|
# filtering by server_id as well.
|
||||||
RemoteRealmCount: remote_realm_id,
|
RemoteRealmCount: remote_realm_id,
|
||||||
@@ -407,7 +390,7 @@ def get_realm_day_counts() -> Dict[str, Dict[str, str]]:
|
|||||||
query = '''
|
query = '''
|
||||||
select
|
select
|
||||||
r.string_id,
|
r.string_id,
|
||||||
(now()::date - date_sent::date) age,
|
(now()::date - pub_date::date) age,
|
||||||
count(*) cnt
|
count(*) cnt
|
||||||
from zerver_message m
|
from zerver_message m
|
||||||
join zerver_userprofile up on up.id = m.sender_id
|
join zerver_userprofile up on up.id = m.sender_id
|
||||||
@@ -416,7 +399,7 @@ def get_realm_day_counts() -> Dict[str, Dict[str, str]]:
|
|||||||
where
|
where
|
||||||
(not up.is_bot)
|
(not up.is_bot)
|
||||||
and
|
and
|
||||||
date_sent > now()::date - interval '8 day'
|
pub_date > now()::date - interval '8 day'
|
||||||
and
|
and
|
||||||
c.name not in ('zephyr_mirror', 'ZulipMonitoring')
|
c.name not in ('zephyr_mirror', 'ZulipMonitoring')
|
||||||
group by
|
group by
|
||||||
@@ -459,9 +442,6 @@ def get_realm_day_counts() -> Dict[str, Dict[str, str]]:
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def get_plan_name(plan_type: int) -> str:
|
|
||||||
return ['', 'self hosted', 'limited', 'standard', 'open source'][plan_type]
|
|
||||||
|
|
||||||
def realm_summary_table(realm_minutes: Dict[str, float]) -> str:
|
def realm_summary_table(realm_minutes: Dict[str, float]) -> str:
|
||||||
now = timezone_now()
|
now = timezone_now()
|
||||||
|
|
||||||
@@ -522,7 +502,7 @@ def realm_summary_table(realm_minutes: Dict[str, float]) -> str:
|
|||||||
FROM (
|
FROM (
|
||||||
SELECT
|
SELECT
|
||||||
realm.id as realm_id,
|
realm.id as realm_id,
|
||||||
up.delivery_email
|
up.email
|
||||||
FROM zerver_useractivity ua
|
FROM zerver_useractivity ua
|
||||||
JOIN zerver_userprofile up
|
JOIN zerver_userprofile up
|
||||||
ON up.id = ua.user_profile_id
|
ON up.id = ua.user_profile_id
|
||||||
@@ -539,7 +519,7 @@ def realm_summary_table(realm_minutes: Dict[str, float]) -> str:
|
|||||||
'/json/users/me/pointer',
|
'/json/users/me/pointer',
|
||||||
'update_pointer_backend'
|
'update_pointer_backend'
|
||||||
)
|
)
|
||||||
GROUP by realm.id, up.delivery_email
|
GROUP by realm.id, up.email
|
||||||
HAVING max(last_visit) > now() - interval '7 day'
|
HAVING max(last_visit) > now() - interval '7 day'
|
||||||
) as wau_users
|
) as wau_users
|
||||||
GROUP BY realm_id
|
GROUP BY realm_id
|
||||||
@@ -577,14 +557,15 @@ def realm_summary_table(realm_minutes: Dict[str, float]) -> str:
|
|||||||
# Fetch all the realm administrator users
|
# Fetch all the realm administrator users
|
||||||
realm_admins = defaultdict(list) # type: Dict[str, List[str]]
|
realm_admins = defaultdict(list) # type: Dict[str, List[str]]
|
||||||
for up in UserProfile.objects.select_related("realm").filter(
|
for up in UserProfile.objects.select_related("realm").filter(
|
||||||
role=UserProfile.ROLE_REALM_ADMINISTRATOR,
|
is_realm_admin=True,
|
||||||
is_active=True
|
is_active=True
|
||||||
):
|
):
|
||||||
realm_admins[up.realm.string_id].append(up.delivery_email)
|
realm_admins[up.realm.string_id].append(up.email)
|
||||||
|
|
||||||
for row in rows:
|
for row in rows:
|
||||||
row['date_created_day'] = row['date_created'].strftime('%Y-%m-%d')
|
row['date_created_day'] = row['date_created'].strftime('%Y-%m-%d')
|
||||||
row['plan_type_string'] = get_plan_name(row['plan_type'])
|
row['plan_type_string'] = [
|
||||||
|
'', 'self hosted', 'limited', 'standard', 'open source'][row['plan_type']]
|
||||||
row['age_days'] = int((now - row['date_created']).total_seconds()
|
row['age_days'] = int((now - row['date_created']).total_seconds()
|
||||||
/ 86400)
|
/ 86400)
|
||||||
row['is_new'] = row['age_days'] < 12 * 7
|
row['is_new'] = row['age_days'] < 12 * 7
|
||||||
@@ -740,7 +721,7 @@ def sent_messages_report(realm: str) -> str:
|
|||||||
) as series
|
) as series
|
||||||
left join (
|
left join (
|
||||||
select
|
select
|
||||||
date_sent::date date_sent,
|
pub_date::date pub_date,
|
||||||
count(*) cnt
|
count(*) cnt
|
||||||
from zerver_message m
|
from zerver_message m
|
||||||
join zerver_userprofile up on up.id = m.sender_id
|
join zerver_userprofile up on up.id = m.sender_id
|
||||||
@@ -750,16 +731,16 @@ def sent_messages_report(realm: str) -> str:
|
|||||||
and
|
and
|
||||||
(not up.is_bot)
|
(not up.is_bot)
|
||||||
and
|
and
|
||||||
date_sent > now() - interval '2 week'
|
pub_date > now() - interval '2 week'
|
||||||
group by
|
group by
|
||||||
date_sent::date
|
pub_date::date
|
||||||
order by
|
order by
|
||||||
date_sent::date
|
pub_date::date
|
||||||
) humans on
|
) humans on
|
||||||
series.day = humans.date_sent
|
series.day = humans.pub_date
|
||||||
left join (
|
left join (
|
||||||
select
|
select
|
||||||
date_sent::date date_sent,
|
pub_date::date pub_date,
|
||||||
count(*) cnt
|
count(*) cnt
|
||||||
from zerver_message m
|
from zerver_message m
|
||||||
join zerver_userprofile up on up.id = m.sender_id
|
join zerver_userprofile up on up.id = m.sender_id
|
||||||
@@ -769,13 +750,13 @@ def sent_messages_report(realm: str) -> str:
|
|||||||
and
|
and
|
||||||
up.is_bot
|
up.is_bot
|
||||||
and
|
and
|
||||||
date_sent > now() - interval '2 week'
|
pub_date > now() - interval '2 week'
|
||||||
group by
|
group by
|
||||||
date_sent::date
|
pub_date::date
|
||||||
order by
|
order by
|
||||||
date_sent::date
|
pub_date::date
|
||||||
) bots on
|
) bots on
|
||||||
series.day = bots.date_sent
|
series.day = bots.pub_date
|
||||||
'''
|
'''
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
cursor.execute(query, [realm, realm])
|
cursor.execute(query, [realm, realm])
|
||||||
@@ -1030,134 +1011,6 @@ def get_activity(request: HttpRequest) -> HttpResponse:
|
|||||||
context=dict(data=data, title=title, is_home=True),
|
context=dict(data=data, title=title, is_home=True),
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_confirmations(types: List[int], object_ids: List[int],
|
|
||||||
hostname: Optional[str]=None) -> List[Dict[str, Any]]:
|
|
||||||
lowest_datetime = timezone_now() - timedelta(days=30)
|
|
||||||
confirmations = Confirmation.objects.filter(type__in=types, object_id__in=object_ids,
|
|
||||||
date_sent__gte=lowest_datetime)
|
|
||||||
confirmation_dicts = []
|
|
||||||
for confirmation in confirmations:
|
|
||||||
realm = confirmation.realm
|
|
||||||
content_object = confirmation.content_object
|
|
||||||
|
|
||||||
if realm is not None:
|
|
||||||
realm_host = realm.host
|
|
||||||
elif isinstance(content_object, Realm):
|
|
||||||
realm_host = content_object.host
|
|
||||||
else:
|
|
||||||
realm_host = hostname
|
|
||||||
|
|
||||||
type = confirmation.type
|
|
||||||
days_to_activate = _properties[type].validity_in_days
|
|
||||||
expiry_date = confirmation.date_sent + timedelta(days=days_to_activate)
|
|
||||||
|
|
||||||
if hasattr(content_object, "status"):
|
|
||||||
if content_object.status == STATUS_ACTIVE:
|
|
||||||
link_status = "Link has been clicked"
|
|
||||||
else:
|
|
||||||
link_status = "Link has never been clicked"
|
|
||||||
else:
|
|
||||||
link_status = ""
|
|
||||||
|
|
||||||
if timezone_now() < expiry_date:
|
|
||||||
expires_in = timesince(confirmation.date_sent, expiry_date)
|
|
||||||
else:
|
|
||||||
expires_in = "Expired"
|
|
||||||
|
|
||||||
url = confirmation_url(confirmation.confirmation_key, realm_host, type)
|
|
||||||
confirmation_dicts.append({"object": confirmation.content_object,
|
|
||||||
"url": url, "type": type, "link_status": link_status,
|
|
||||||
"expires_in": expires_in})
|
|
||||||
return confirmation_dicts
|
|
||||||
|
|
||||||
@require_server_admin
|
|
||||||
def support(request: HttpRequest) -> HttpResponse:
|
|
||||||
context = {} # type: Dict[str, Any]
|
|
||||||
if settings.BILLING_ENABLED and request.method == "POST":
|
|
||||||
realm_id = request.POST.get("realm_id", None)
|
|
||||||
realm = Realm.objects.get(id=realm_id)
|
|
||||||
|
|
||||||
new_plan_type = request.POST.get("plan_type", None)
|
|
||||||
if new_plan_type is not None:
|
|
||||||
new_plan_type = int(new_plan_type)
|
|
||||||
current_plan_type = realm.plan_type
|
|
||||||
do_change_plan_type(realm, new_plan_type)
|
|
||||||
msg = "Plan type of {} changed from {} to {} ".format(realm.name,
|
|
||||||
get_plan_name(current_plan_type),
|
|
||||||
get_plan_name(new_plan_type))
|
|
||||||
context["message"] = msg
|
|
||||||
|
|
||||||
new_discount = request.POST.get("discount", None)
|
|
||||||
if new_discount is not None:
|
|
||||||
new_discount = Decimal(new_discount)
|
|
||||||
current_discount = get_discount_for_realm(realm)
|
|
||||||
attach_discount_to_realm(realm, new_discount)
|
|
||||||
msg = "Discount of {} changed to {} from {} ".format(realm.name, new_discount, current_discount)
|
|
||||||
context["message"] = msg
|
|
||||||
|
|
||||||
status = request.POST.get("status", None)
|
|
||||||
if status is not None:
|
|
||||||
if status == "active":
|
|
||||||
do_send_realm_reactivation_email(realm)
|
|
||||||
context["message"] = "Realm reactivation email sent to admins of {}.".format(realm.name)
|
|
||||||
elif status == "deactivated":
|
|
||||||
do_deactivate_realm(realm, request.user)
|
|
||||||
context["message"] = "{} deactivated.".format(realm.name)
|
|
||||||
|
|
||||||
scrub_realm = request.POST.get("scrub_realm", None)
|
|
||||||
if scrub_realm is not None:
|
|
||||||
if scrub_realm == "scrub_realm":
|
|
||||||
do_scrub_realm(realm)
|
|
||||||
context["message"] = "{} scrubbed.".format(realm.name)
|
|
||||||
|
|
||||||
query = request.GET.get("q", None)
|
|
||||||
if query:
|
|
||||||
key_words = get_invitee_emails_set(query)
|
|
||||||
|
|
||||||
context["users"] = UserProfile.objects.filter(delivery_email__in=key_words)
|
|
||||||
realms = set(Realm.objects.filter(string_id__in=key_words))
|
|
||||||
|
|
||||||
for key_word in key_words:
|
|
||||||
try:
|
|
||||||
URLValidator()(key_word)
|
|
||||||
parse_result = urllib.parse.urlparse(key_word)
|
|
||||||
hostname = parse_result.hostname
|
|
||||||
assert hostname is not None
|
|
||||||
if parse_result.port:
|
|
||||||
hostname = "{}:{}".format(hostname, parse_result.port)
|
|
||||||
subdomain = get_subdomain_from_hostname(hostname)
|
|
||||||
try:
|
|
||||||
realms.add(get_realm(subdomain))
|
|
||||||
except Realm.DoesNotExist:
|
|
||||||
pass
|
|
||||||
except ValidationError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
context["realms"] = realms
|
|
||||||
|
|
||||||
confirmations = [] # type: List[Dict[str, Any]]
|
|
||||||
|
|
||||||
preregistration_users = PreregistrationUser.objects.filter(email__in=key_words)
|
|
||||||
confirmations += get_confirmations([Confirmation.USER_REGISTRATION, Confirmation.INVITATION,
|
|
||||||
Confirmation.REALM_CREATION], preregistration_users,
|
|
||||||
hostname=request.get_host())
|
|
||||||
|
|
||||||
multiuse_invites = MultiuseInvite.objects.filter(realm__in=realms)
|
|
||||||
confirmations += get_confirmations([Confirmation.MULTIUSE_INVITE], multiuse_invites)
|
|
||||||
|
|
||||||
confirmations += get_confirmations([Confirmation.REALM_REACTIVATION], [realm.id for realm in realms])
|
|
||||||
|
|
||||||
context["confirmations"] = confirmations
|
|
||||||
|
|
||||||
def realm_admin_emails(realm: Realm) -> str:
|
|
||||||
return ", ".join(realm.get_human_admin_users().values_list("delivery_email", flat=True))
|
|
||||||
|
|
||||||
context["realm_admin_emails"] = realm_admin_emails
|
|
||||||
context["get_discount_for_realm"] = get_discount_for_realm
|
|
||||||
context["realm_icon_url"] = realm_icon_url
|
|
||||||
context["Confirmation"] = Confirmation
|
|
||||||
return render(request, 'analytics/support.html', context=context)
|
|
||||||
|
|
||||||
def get_user_activity_records_for_realm(realm: str, is_bot: bool) -> QuerySet:
|
def get_user_activity_records_for_realm(realm: str, is_bot: bool) -> QuerySet:
|
||||||
fields = [
|
fields = [
|
||||||
'user_profile__full_name',
|
'user_profile__full_name',
|
||||||
@@ -1283,7 +1136,7 @@ def realm_activity_link(realm_str: str) -> mark_safe:
|
|||||||
def realm_stats_link(realm_str: str) -> mark_safe:
|
def realm_stats_link(realm_str: str) -> mark_safe:
|
||||||
url_name = 'analytics.views.stats_for_realm'
|
url_name = 'analytics.views.stats_for_realm'
|
||||||
url = reverse(url_name, kwargs=dict(realm_str=realm_str))
|
url = reverse(url_name, kwargs=dict(realm_str=realm_str))
|
||||||
stats_link = '<a href="{}"><i class="fa fa-pie-chart"></i>{}</a>'.format(url, realm_str)
|
stats_link = '<a href="{}"><i class="fa fa-pie-chart"></i></a>'.format(url, realm_str)
|
||||||
return mark_safe(stats_link)
|
return mark_safe(stats_link)
|
||||||
|
|
||||||
def remote_installation_stats_link(server_id: int, hostname: str) -> mark_safe:
|
def remote_installation_stats_link(server_id: int, hostname: str) -> mark_safe:
|
||||||
@@ -1434,7 +1287,7 @@ def get_realm_activity(request: HttpRequest, realm_str: str) -> HttpResponse:
|
|||||||
all_user_records = {} # type: Dict[str, Any]
|
all_user_records = {} # type: Dict[str, Any]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
admins = Realm.objects.get(string_id=realm_str).get_human_admin_users()
|
admins = Realm.objects.get(string_id=realm_str).get_admin_users()
|
||||||
except Realm.DoesNotExist:
|
except Realm.DoesNotExist:
|
||||||
return HttpResponseNotFound("Realm %s does not exist" % (realm_str,))
|
return HttpResponseNotFound("Realm %s does not exist" % (realm_str,))
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
presets: [
|
|
||||||
[
|
|
||||||
"@babel/preset-env",
|
|
||||||
{
|
|
||||||
corejs: 3,
|
|
||||||
useBuiltIns: "usage",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
"@babel/typescript",
|
|
||||||
],
|
|
||||||
plugins: [
|
|
||||||
"@babel/proposal-class-properties",
|
|
||||||
["@babel/plugin-proposal-unicode-property-regex", { useUnicodeFlag: false }],
|
|
||||||
],
|
|
||||||
sourceType: "unambiguous",
|
|
||||||
};
|
|
||||||
@@ -72,9 +72,6 @@ def create_confirmation_link(obj: ContentType, host: str,
|
|||||||
realm = None
|
realm = None
|
||||||
if hasattr(obj, 'realm'):
|
if hasattr(obj, 'realm'):
|
||||||
realm = obj.realm
|
realm = obj.realm
|
||||||
elif isinstance(obj, Realm):
|
|
||||||
realm = obj
|
|
||||||
|
|
||||||
Confirmation.objects.create(content_object=obj, date_sent=timezone_now(), confirmation_key=key,
|
Confirmation.objects.create(content_object=obj, date_sent=timezone_now(), confirmation_key=key,
|
||||||
realm=realm, type=confirmation_type)
|
realm=realm, type=confirmation_type)
|
||||||
return confirmation_url(key, host, confirmation_type, url_args)
|
return confirmation_url(key, host, confirmation_type, url_args)
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ from zerver.lib.timestamp import datetime_to_timestamp, timestamp_to_datetime
|
|||||||
from zerver.lib.utils import generate_random_token
|
from zerver.lib.utils import generate_random_token
|
||||||
from zerver.models import Realm, UserProfile, RealmAuditLog
|
from zerver.models import Realm, UserProfile, RealmAuditLog
|
||||||
from corporate.models import Customer, CustomerPlan, LicenseLedger, \
|
from corporate.models import Customer, CustomerPlan, LicenseLedger, \
|
||||||
get_current_plan
|
get_active_plan
|
||||||
from zproject.config import get_secret
|
from zproject.settings import get_secret
|
||||||
|
|
||||||
STRIPE_PUBLISHABLE_KEY = get_secret('stripe_publishable_key')
|
STRIPE_PUBLISHABLE_KEY = get_secret('stripe_publishable_key')
|
||||||
stripe.api_key = get_secret('stripe_secret_key')
|
stripe.api_key = get_secret('stripe_secret_key')
|
||||||
@@ -38,11 +38,11 @@ CallableT = TypeVar('CallableT', bound=Callable[..., Any])
|
|||||||
MIN_INVOICED_LICENSES = 30
|
MIN_INVOICED_LICENSES = 30
|
||||||
DEFAULT_INVOICE_DAYS_UNTIL_DUE = 30
|
DEFAULT_INVOICE_DAYS_UNTIL_DUE = 30
|
||||||
|
|
||||||
def get_latest_seat_count(realm: Realm) -> int:
|
def get_seat_count(realm: Realm) -> int:
|
||||||
non_guests = UserProfile.objects.filter(
|
non_guests = UserProfile.objects.filter(
|
||||||
realm=realm, is_active=True, is_bot=False).exclude(role=UserProfile.ROLE_GUEST).count()
|
realm=realm, is_active=True, is_bot=False, is_guest=False).count()
|
||||||
guests = UserProfile.objects.filter(
|
guests = UserProfile.objects.filter(
|
||||||
realm=realm, is_active=True, is_bot=False, role=UserProfile.ROLE_GUEST).count()
|
realm=realm, is_active=True, is_bot=False, is_guest=True).count()
|
||||||
return max(non_guests, math.ceil(guests / 5))
|
return max(non_guests, math.ceil(guests / 5))
|
||||||
|
|
||||||
def sign_string(string: str) -> Tuple[str, str]:
|
def sign_string(string: str) -> Tuple[str, str]:
|
||||||
@@ -82,7 +82,8 @@ def next_month(billing_cycle_anchor: datetime, dt: datetime) -> datetime:
|
|||||||
raise AssertionError('Something wrong in next_month calculation with '
|
raise AssertionError('Something wrong in next_month calculation with '
|
||||||
'billing_cycle_anchor: %s, dt: %s' % (billing_cycle_anchor, dt))
|
'billing_cycle_anchor: %s, dt: %s' % (billing_cycle_anchor, dt))
|
||||||
|
|
||||||
def start_of_next_billing_cycle(plan: CustomerPlan, event_time: datetime) -> datetime:
|
# TODO take downgrade into account
|
||||||
|
def next_renewal_date(plan: CustomerPlan, event_time: datetime) -> datetime:
|
||||||
months_per_period = {
|
months_per_period = {
|
||||||
CustomerPlan.ANNUAL: 12,
|
CustomerPlan.ANNUAL: 12,
|
||||||
CustomerPlan.MONTHLY: 1,
|
CustomerPlan.MONTHLY: 1,
|
||||||
@@ -94,10 +95,8 @@ def start_of_next_billing_cycle(plan: CustomerPlan, event_time: datetime) -> dat
|
|||||||
periods += 1
|
periods += 1
|
||||||
return dt
|
return dt
|
||||||
|
|
||||||
def next_invoice_date(plan: CustomerPlan) -> Optional[datetime]:
|
# TODO take downgrade into account
|
||||||
if plan.status == CustomerPlan.ENDED:
|
def next_invoice_date(plan: CustomerPlan) -> datetime:
|
||||||
return None
|
|
||||||
assert(plan.next_invoice_date is not None) # for mypy
|
|
||||||
months_per_period = {
|
months_per_period = {
|
||||||
CustomerPlan.ANNUAL: 12,
|
CustomerPlan.ANNUAL: 12,
|
||||||
CustomerPlan.MONTHLY: 1,
|
CustomerPlan.MONTHLY: 1,
|
||||||
@@ -111,20 +110,18 @@ def next_invoice_date(plan: CustomerPlan) -> Optional[datetime]:
|
|||||||
periods += 1
|
periods += 1
|
||||||
return dt
|
return dt
|
||||||
|
|
||||||
def renewal_amount(plan: CustomerPlan, event_time: datetime) -> int: # nocoverage: TODO
|
def renewal_amount(plan: CustomerPlan, event_time: datetime) -> Optional[int]: # nocoverage: TODO
|
||||||
if plan.fixed_price is not None:
|
if plan.fixed_price is not None:
|
||||||
return plan.fixed_price
|
return plan.fixed_price
|
||||||
last_ledger_entry = make_end_of_cycle_updates_if_needed(plan, event_time)
|
last_ledger_entry = add_plan_renewal_to_license_ledger_if_needed(plan, event_time)
|
||||||
if last_ledger_entry is None:
|
|
||||||
return 0
|
|
||||||
if last_ledger_entry.licenses_at_next_renewal is None:
|
if last_ledger_entry.licenses_at_next_renewal is None:
|
||||||
return 0
|
return None
|
||||||
assert(plan.price_per_license is not None) # for mypy
|
assert(plan.price_per_license is not None) # for mypy
|
||||||
return plan.price_per_license * last_ledger_entry.licenses_at_next_renewal
|
return plan.price_per_license * last_ledger_entry.licenses_at_next_renewal
|
||||||
|
|
||||||
class BillingError(Exception):
|
class BillingError(Exception):
|
||||||
# error messages
|
# error messages
|
||||||
CONTACT_SUPPORT = _("Something went wrong. Please contact %s.") % (settings.ZULIP_ADMINISTRATOR,)
|
CONTACT_SUPPORT = _("Something went wrong. Please contact %s." % (settings.ZULIP_ADMINISTRATOR,))
|
||||||
TRY_RELOADING = _("Something went wrong. Please reload the page.")
|
TRY_RELOADING = _("Something went wrong. Please reload the page.")
|
||||||
|
|
||||||
# description is used only for tests
|
# description is used only for tests
|
||||||
@@ -178,7 +175,7 @@ def do_create_stripe_customer(user: UserProfile, stripe_token: Optional[str]=Non
|
|||||||
# customer that we can delete or ignore.
|
# customer that we can delete or ignore.
|
||||||
stripe_customer = stripe.Customer.create(
|
stripe_customer = stripe.Customer.create(
|
||||||
description="%s (%s)" % (realm.string_id, realm.name),
|
description="%s (%s)" % (realm.string_id, realm.name),
|
||||||
email=user.delivery_email,
|
email=user.email,
|
||||||
metadata={'realm_id': realm.id, 'realm_str': realm.string_id},
|
metadata={'realm_id': realm.id, 'realm_str': realm.string_id},
|
||||||
source=stripe_token)
|
source=stripe_token)
|
||||||
event_time = timestamp_to_datetime(stripe_customer.created)
|
event_time = timestamp_to_datetime(stripe_customer.created)
|
||||||
@@ -197,42 +194,31 @@ def do_create_stripe_customer(user: UserProfile, stripe_token: Optional[str]=Non
|
|||||||
return customer
|
return customer
|
||||||
|
|
||||||
@catch_stripe_errors
|
@catch_stripe_errors
|
||||||
def do_replace_payment_source(user: UserProfile, stripe_token: str,
|
def do_replace_payment_source(user: UserProfile, stripe_token: str) -> stripe.Customer:
|
||||||
pay_invoices: bool=False) -> stripe.Customer:
|
|
||||||
stripe_customer = stripe_get_customer(Customer.objects.get(realm=user.realm).stripe_customer_id)
|
stripe_customer = stripe_get_customer(Customer.objects.get(realm=user.realm).stripe_customer_id)
|
||||||
stripe_customer.source = stripe_token
|
stripe_customer.source = stripe_token
|
||||||
# Deletes existing card: https://stripe.com/docs/api#update_customer-source
|
# Deletes existing card: https://stripe.com/docs/api#update_customer-source
|
||||||
|
# This can also have other side effects, e.g. it will try to pay certain past-due
|
||||||
|
# invoices: https://stripe.com/docs/api#update_customer
|
||||||
updated_stripe_customer = stripe.Customer.save(stripe_customer)
|
updated_stripe_customer = stripe.Customer.save(stripe_customer)
|
||||||
RealmAuditLog.objects.create(
|
RealmAuditLog.objects.create(
|
||||||
realm=user.realm, acting_user=user, event_type=RealmAuditLog.STRIPE_CARD_CHANGED,
|
realm=user.realm, acting_user=user, event_type=RealmAuditLog.STRIPE_CARD_CHANGED,
|
||||||
event_time=timezone_now())
|
event_time=timezone_now())
|
||||||
if pay_invoices:
|
|
||||||
for stripe_invoice in stripe.Invoice.list(
|
|
||||||
billing='charge_automatically', customer=stripe_customer.id, status='open'):
|
|
||||||
# The user will get either a receipt or a "failed payment" email, but the in-app
|
|
||||||
# messaging could be clearer here (e.g. it could explictly tell the user that there
|
|
||||||
# were payment(s) and that they succeeded or failed).
|
|
||||||
# Worth fixing if we notice that a lot of cards end up failing at this step.
|
|
||||||
stripe.Invoice.pay(stripe_invoice)
|
|
||||||
return updated_stripe_customer
|
return updated_stripe_customer
|
||||||
|
|
||||||
# event_time should roughly be timezone_now(). Not designed to handle
|
# event_time should roughly be timezone_now(). Not designed to handle
|
||||||
# event_times in the past or future
|
# event_times in the past or future
|
||||||
def make_end_of_cycle_updates_if_needed(plan: CustomerPlan,
|
# TODO handle downgrade
|
||||||
event_time: datetime) -> Optional[LicenseLedger]:
|
def add_plan_renewal_to_license_ledger_if_needed(plan: CustomerPlan, event_time: datetime) -> LicenseLedger:
|
||||||
last_ledger_entry = LicenseLedger.objects.filter(plan=plan).order_by('-id').first()
|
last_ledger_entry = LicenseLedger.objects.filter(plan=plan).order_by('-id').first()
|
||||||
last_renewal = LicenseLedger.objects.filter(plan=plan, is_renewal=True) \
|
last_renewal = LicenseLedger.objects.filter(plan=plan, is_renewal=True) \
|
||||||
.order_by('-id').first().event_time
|
.order_by('-id').first().event_time
|
||||||
next_billing_cycle = start_of_next_billing_cycle(plan, last_renewal)
|
plan_renewal_date = next_renewal_date(plan, last_renewal)
|
||||||
if next_billing_cycle <= event_time:
|
if plan_renewal_date <= event_time:
|
||||||
if plan.status == CustomerPlan.ACTIVE:
|
return LicenseLedger.objects.create(
|
||||||
return LicenseLedger.objects.create(
|
plan=plan, is_renewal=True, event_time=plan_renewal_date,
|
||||||
plan=plan, is_renewal=True, event_time=next_billing_cycle,
|
licenses=last_ledger_entry.licenses_at_next_renewal,
|
||||||
licenses=last_ledger_entry.licenses_at_next_renewal,
|
licenses_at_next_renewal=last_ledger_entry.licenses_at_next_renewal)
|
||||||
licenses_at_next_renewal=last_ledger_entry.licenses_at_next_renewal)
|
|
||||||
if plan.status == CustomerPlan.DOWNGRADE_AT_END_OF_CYCLE:
|
|
||||||
process_downgrade(plan)
|
|
||||||
return None
|
|
||||||
return last_ledger_entry
|
return last_ledger_entry
|
||||||
|
|
||||||
# Returns Customer instead of stripe_customer so that we don't make a Stripe
|
# Returns Customer instead of stripe_customer so that we don't make a Stripe
|
||||||
@@ -276,7 +262,7 @@ def process_initial_upgrade(user: UserProfile, licenses: int, automanage_license
|
|||||||
billing_schedule: int, stripe_token: Optional[str]) -> None:
|
billing_schedule: int, stripe_token: Optional[str]) -> None:
|
||||||
realm = user.realm
|
realm = user.realm
|
||||||
customer = update_or_create_stripe_customer(user, stripe_token=stripe_token)
|
customer = update_or_create_stripe_customer(user, stripe_token=stripe_token)
|
||||||
if get_current_plan(customer) is not None:
|
if CustomerPlan.objects.filter(customer=customer, status=CustomerPlan.ACTIVE).exists():
|
||||||
# Unlikely race condition from two people upgrading (clicking "Make payment")
|
# Unlikely race condition from two people upgrading (clicking "Make payment")
|
||||||
# at exactly the same time. Doesn't fully resolve the race condition, but having
|
# at exactly the same time. Doesn't fully resolve the race condition, but having
|
||||||
# a check here reduces the likelihood.
|
# a check here reduces the likelihood.
|
||||||
@@ -298,7 +284,7 @@ def process_initial_upgrade(user: UserProfile, licenses: int, automanage_license
|
|||||||
currency='usd',
|
currency='usd',
|
||||||
customer=customer.stripe_customer_id,
|
customer=customer.stripe_customer_id,
|
||||||
description="Upgrade to Zulip Standard, ${} x {}".format(price_per_license/100, licenses),
|
description="Upgrade to Zulip Standard, ${} x {}".format(price_per_license/100, licenses),
|
||||||
receipt_email=user.delivery_email,
|
receipt_email=user.email,
|
||||||
statement_descriptor='Zulip Standard')
|
statement_descriptor='Zulip Standard')
|
||||||
# Not setting a period start and end, but maybe we should? Unclear what will make things
|
# Not setting a period start and end, but maybe we should? Unclear what will make things
|
||||||
# most similar to the renewal case from an accounting perspective.
|
# most similar to the renewal case from an accounting perspective.
|
||||||
@@ -314,7 +300,7 @@ def process_initial_upgrade(user: UserProfile, licenses: int, automanage_license
|
|||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
# billed_licenses can greater than licenses if users are added between the start of
|
# billed_licenses can greater than licenses if users are added between the start of
|
||||||
# this function (process_initial_upgrade) and now
|
# this function (process_initial_upgrade) and now
|
||||||
billed_licenses = max(get_latest_seat_count(realm), licenses)
|
billed_licenses = max(get_seat_count(realm), licenses)
|
||||||
plan_params = {
|
plan_params = {
|
||||||
'automanage_licenses': automanage_licenses,
|
'automanage_licenses': automanage_licenses,
|
||||||
'charge_automatically': charge_automatically,
|
'charge_automatically': charge_automatically,
|
||||||
@@ -368,10 +354,9 @@ def process_initial_upgrade(user: UserProfile, licenses: int, automanage_license
|
|||||||
|
|
||||||
def update_license_ledger_for_automanaged_plan(realm: Realm, plan: CustomerPlan,
|
def update_license_ledger_for_automanaged_plan(realm: Realm, plan: CustomerPlan,
|
||||||
event_time: datetime) -> None:
|
event_time: datetime) -> None:
|
||||||
last_ledger_entry = make_end_of_cycle_updates_if_needed(plan, event_time)
|
last_ledger_entry = add_plan_renewal_to_license_ledger_if_needed(plan, event_time)
|
||||||
if last_ledger_entry is None:
|
# todo: handle downgrade, where licenses_at_next_renewal should be 0
|
||||||
return
|
licenses_at_next_renewal = get_seat_count(realm)
|
||||||
licenses_at_next_renewal = get_latest_seat_count(realm)
|
|
||||||
licenses = max(licenses_at_next_renewal, last_ledger_entry.licenses)
|
licenses = max(licenses_at_next_renewal, last_ledger_entry.licenses)
|
||||||
LicenseLedger.objects.create(
|
LicenseLedger.objects.create(
|
||||||
plan=plan, event_time=event_time, licenses=licenses,
|
plan=plan, event_time=event_time, licenses=licenses,
|
||||||
@@ -381,7 +366,7 @@ def update_license_ledger_if_needed(realm: Realm, event_time: datetime) -> None:
|
|||||||
customer = Customer.objects.filter(realm=realm).first()
|
customer = Customer.objects.filter(realm=realm).first()
|
||||||
if customer is None:
|
if customer is None:
|
||||||
return
|
return
|
||||||
plan = get_current_plan(customer)
|
plan = get_active_plan(customer)
|
||||||
if plan is None:
|
if plan is None:
|
||||||
return
|
return
|
||||||
if not plan.automanage_licenses:
|
if not plan.automanage_licenses:
|
||||||
@@ -391,7 +376,7 @@ def update_license_ledger_if_needed(realm: Realm, event_time: datetime) -> None:
|
|||||||
def invoice_plan(plan: CustomerPlan, event_time: datetime) -> None:
|
def invoice_plan(plan: CustomerPlan, event_time: datetime) -> None:
|
||||||
if plan.invoicing_status == CustomerPlan.STARTED:
|
if plan.invoicing_status == CustomerPlan.STARTED:
|
||||||
raise NotImplementedError('Plan with invoicing_status==STARTED needs manual resolution.')
|
raise NotImplementedError('Plan with invoicing_status==STARTED needs manual resolution.')
|
||||||
make_end_of_cycle_updates_if_needed(plan, event_time)
|
add_plan_renewal_to_license_ledger_if_needed(plan, event_time)
|
||||||
assert(plan.invoiced_through is not None)
|
assert(plan.invoiced_through is not None)
|
||||||
licenses_base = plan.invoiced_through.licenses
|
licenses_base = plan.invoiced_through.licenses
|
||||||
invoice_item_created = False
|
invoice_item_created = False
|
||||||
@@ -411,7 +396,7 @@ def invoice_plan(plan: CustomerPlan, event_time: datetime) -> None:
|
|||||||
last_renewal = LicenseLedger.objects.filter(
|
last_renewal = LicenseLedger.objects.filter(
|
||||||
plan=plan, is_renewal=True, event_time__lte=ledger_entry.event_time) \
|
plan=plan, is_renewal=True, event_time__lte=ledger_entry.event_time) \
|
||||||
.order_by('-id').first().event_time
|
.order_by('-id').first().event_time
|
||||||
period_end = start_of_next_billing_cycle(plan, ledger_entry.event_time)
|
period_end = next_renewal_date(plan, ledger_entry.event_time)
|
||||||
proration_fraction = (period_end - ledger_entry.event_time) / (period_end - last_renewal)
|
proration_fraction = (period_end - ledger_entry.event_time) / (period_end - last_renewal)
|
||||||
price_args = {'unit_amount': int(plan.price_per_license * proration_fraction + .5),
|
price_args = {'unit_amount': int(plan.price_per_license * proration_fraction + .5),
|
||||||
'quantity': ledger_entry.licenses - licenses_base}
|
'quantity': ledger_entry.licenses - licenses_base}
|
||||||
@@ -431,8 +416,7 @@ def invoice_plan(plan: CustomerPlan, event_time: datetime) -> None:
|
|||||||
description=description,
|
description=description,
|
||||||
discountable=False,
|
discountable=False,
|
||||||
period = {'start': datetime_to_timestamp(ledger_entry.event_time),
|
period = {'start': datetime_to_timestamp(ledger_entry.event_time),
|
||||||
'end': datetime_to_timestamp(
|
'end': datetime_to_timestamp(next_renewal_date(plan, ledger_entry.event_time))},
|
||||||
start_of_next_billing_cycle(plan, ledger_entry.event_time))},
|
|
||||||
idempotency_key=idempotency_key,
|
idempotency_key=idempotency_key,
|
||||||
**price_args)
|
**price_args)
|
||||||
invoice_item_created = True
|
invoice_item_created = True
|
||||||
@@ -459,30 +443,15 @@ def invoice_plan(plan: CustomerPlan, event_time: datetime) -> None:
|
|||||||
plan.next_invoice_date = next_invoice_date(plan)
|
plan.next_invoice_date = next_invoice_date(plan)
|
||||||
plan.save(update_fields=['next_invoice_date'])
|
plan.save(update_fields=['next_invoice_date'])
|
||||||
|
|
||||||
def invoice_plans_as_needed(event_time: datetime=timezone_now()) -> None:
|
def invoice_plans_as_needed(event_time: datetime) -> None:
|
||||||
for plan in CustomerPlan.objects.filter(next_invoice_date__lte=event_time):
|
for plan in CustomerPlan.objects.filter(next_invoice_date__lte=event_time):
|
||||||
invoice_plan(plan, event_time)
|
invoice_plan(plan, event_time)
|
||||||
|
|
||||||
def attach_discount_to_realm(realm: Realm, discount: Decimal) -> None:
|
def attach_discount_to_realm(realm: Realm, discount: Decimal) -> None:
|
||||||
Customer.objects.update_or_create(realm=realm, defaults={'default_discount': discount})
|
Customer.objects.update_or_create(realm=realm, defaults={'default_discount': discount})
|
||||||
|
|
||||||
def get_discount_for_realm(realm: Realm) -> Optional[Decimal]:
|
def process_downgrade(user: UserProfile) -> None: # nocoverage
|
||||||
customer = Customer.objects.filter(realm=realm).first()
|
pass
|
||||||
if customer is not None:
|
|
||||||
return customer.default_discount
|
|
||||||
return None
|
|
||||||
|
|
||||||
def do_change_plan_status(plan: CustomerPlan, status: int) -> None:
|
|
||||||
plan.status = status
|
|
||||||
plan.save(update_fields=['status'])
|
|
||||||
billing_logger.info('Change plan status: Customer.id: %s, CustomerPlan.id: %s, status: %s' % (
|
|
||||||
plan.customer.id, plan.id, status))
|
|
||||||
|
|
||||||
def process_downgrade(plan: CustomerPlan) -> None:
|
|
||||||
from zerver.lib.actions import do_change_plan_type
|
|
||||||
do_change_plan_type(plan.customer.realm, Realm.LIMITED)
|
|
||||||
plan.status = CustomerPlan.ENDED
|
|
||||||
plan.save(update_fields=['status'])
|
|
||||||
|
|
||||||
def estimate_annual_recurring_revenue_by_realm() -> Dict[str, int]: # nocoverage
|
def estimate_annual_recurring_revenue_by_realm() -> Dict[str, int]: # nocoverage
|
||||||
annual_revenue = {}
|
annual_revenue = {}
|
||||||
@@ -490,7 +459,7 @@ def estimate_annual_recurring_revenue_by_realm() -> Dict[str, int]: # nocoverag
|
|||||||
status=CustomerPlan.ACTIVE).select_related('customer__realm'):
|
status=CustomerPlan.ACTIVE).select_related('customer__realm'):
|
||||||
# TODO: figure out what to do for plans that don't automatically
|
# TODO: figure out what to do for plans that don't automatically
|
||||||
# renew, but which probably will renew
|
# renew, but which probably will renew
|
||||||
renewal_cents = renewal_amount(plan, timezone_now())
|
renewal_cents = renewal_amount(plan, timezone_now()) or 0
|
||||||
if plan.billing_schedule == CustomerPlan.MONTHLY:
|
if plan.billing_schedule == CustomerPlan.MONTHLY:
|
||||||
renewal_cents *= 12
|
renewal_cents *= 12
|
||||||
# TODO: Decimal stuff
|
# TODO: Decimal stuff
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Generated by Django 1.11.20 on 2019-04-11 00:45
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('corporate', '0007_remove_deprecated_fields'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='customerplan',
|
|
||||||
name='next_invoice_date',
|
|
||||||
field=models.DateTimeField(db_index=True, null=True),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@@ -35,7 +35,7 @@ class CustomerPlan(models.Model):
|
|||||||
MONTHLY = 2
|
MONTHLY = 2
|
||||||
billing_schedule = models.SmallIntegerField() # type: int
|
billing_schedule = models.SmallIntegerField() # type: int
|
||||||
|
|
||||||
next_invoice_date = models.DateTimeField(db_index=True, null=True) # type: Optional[datetime.datetime]
|
next_invoice_date = models.DateTimeField(db_index=True) # type: datetime.datetime
|
||||||
invoiced_through = models.ForeignKey(
|
invoiced_through = models.ForeignKey(
|
||||||
'LicenseLedger', null=True, on_delete=CASCADE, related_name='+') # type: Optional[LicenseLedger]
|
'LicenseLedger', null=True, on_delete=CASCADE, related_name='+') # type: Optional[LicenseLedger]
|
||||||
DONE = 1
|
DONE = 1
|
||||||
@@ -48,19 +48,15 @@ class CustomerPlan(models.Model):
|
|||||||
tier = models.SmallIntegerField() # type: int
|
tier = models.SmallIntegerField() # type: int
|
||||||
|
|
||||||
ACTIVE = 1
|
ACTIVE = 1
|
||||||
DOWNGRADE_AT_END_OF_CYCLE = 2
|
ENDED = 2
|
||||||
# "Live" plans should have a value < LIVE_STATUS_THRESHOLD.
|
NEVER_STARTED = 3
|
||||||
# There should be at most one live plan per customer.
|
# You can only have 1 active subscription at a time
|
||||||
LIVE_STATUS_THRESHOLD = 10
|
|
||||||
ENDED = 11
|
|
||||||
NEVER_STARTED = 12
|
|
||||||
status = models.SmallIntegerField(default=ACTIVE) # type: int
|
status = models.SmallIntegerField(default=ACTIVE) # type: int
|
||||||
|
|
||||||
# TODO maybe override setattr to ensure billing_cycle_anchor, etc are immutable
|
# TODO maybe override setattr to ensure billing_cycle_anchor, etc are immutable
|
||||||
|
|
||||||
def get_current_plan(customer: Customer) -> Optional[CustomerPlan]:
|
def get_active_plan(customer: Customer) -> Optional[CustomerPlan]:
|
||||||
return CustomerPlan.objects.filter(
|
return CustomerPlan.objects.filter(customer=customer, status=CustomerPlan.ACTIVE).first()
|
||||||
customer=customer, status__lt=CustomerPlan.LIVE_STATUS_THRESHOLD).first()
|
|
||||||
|
|
||||||
class LicenseLedger(models.Model):
|
class LicenseLedger(models.Model):
|
||||||
plan = models.ForeignKey(CustomerPlan, on_delete=CASCADE) # type: CustomerPlan
|
plan = models.ForeignKey(CustomerPlan, on_delete=CASCADE) # type: CustomerPlan
|
||||||
@@ -69,5 +65,6 @@ class LicenseLedger(models.Model):
|
|||||||
event_time = models.DateTimeField() # type: datetime.datetime
|
event_time = models.DateTimeField() # type: datetime.datetime
|
||||||
licenses = models.IntegerField() # type: int
|
licenses = models.IntegerField() # type: int
|
||||||
# None means the plan does not automatically renew.
|
# None means the plan does not automatically renew.
|
||||||
|
# 0 means the plan has been explicitly downgraded.
|
||||||
# This cannot be None if plan.automanage_licenses.
|
# This cannot be None if plan.automanage_licenses.
|
||||||
licenses_at_next_renewal = models.IntegerField(null=True) # type: Optional[int]
|
licenses_at_next_renewal = models.IntegerField(null=True) # type: Optional[int]
|
||||||
|
|||||||
@@ -5,19 +5,6 @@
|
|||||||
"application_fee": null,
|
"application_fee": null,
|
||||||
"application_fee_amount": null,
|
"application_fee_amount": null,
|
||||||
"balance_transaction": "txn_NORMALIZED00000000000001",
|
"balance_transaction": "txn_NORMALIZED00000000000001",
|
||||||
"billing_details": {
|
|
||||||
"address": {
|
|
||||||
"city": "Pacific",
|
|
||||||
"country": "United States",
|
|
||||||
"line1": "Under the sea,",
|
|
||||||
"line2": null,
|
|
||||||
"postal_code": "33333",
|
|
||||||
"state": null
|
|
||||||
},
|
|
||||||
"email": null,
|
|
||||||
"name": "Ada Starr",
|
|
||||||
"phone": null
|
|
||||||
},
|
|
||||||
"captured": true,
|
"captured": true,
|
||||||
"created": 1000000000,
|
"created": 1000000000,
|
||||||
"currency": "usd",
|
"currency": "usd",
|
||||||
@@ -45,26 +32,6 @@
|
|||||||
},
|
},
|
||||||
"paid": true,
|
"paid": true,
|
||||||
"payment_intent": null,
|
"payment_intent": null,
|
||||||
"payment_method": "card_NORMALIZED00000000000001",
|
|
||||||
"payment_method_details": {
|
|
||||||
"card": {
|
|
||||||
"brand": "visa",
|
|
||||||
"checks": {
|
|
||||||
"address_line1_check": "pass",
|
|
||||||
"address_postal_code_check": "pass",
|
|
||||||
"cvc_check": "pass"
|
|
||||||
},
|
|
||||||
"country": "US",
|
|
||||||
"exp_month": 3,
|
|
||||||
"exp_year": 2033,
|
|
||||||
"fingerprint": "NORMALIZED000001",
|
|
||||||
"funding": "credit",
|
|
||||||
"last4": "4242",
|
|
||||||
"three_d_secure": null,
|
|
||||||
"wallet": null
|
|
||||||
},
|
|
||||||
"type": "card"
|
|
||||||
},
|
|
||||||
"receipt_email": "hamlet@zulip.com",
|
"receipt_email": "hamlet@zulip.com",
|
||||||
"receipt_number": null,
|
"receipt_number": null,
|
||||||
"receipt_url": "https://pay.stripe.com/receipts/acct_NORMALIZED000001/ch_NORMALIZED00000000000001/rcpt_NORMALIZED000000000000000000001",
|
"receipt_url": "https://pay.stripe.com/receipts/acct_NORMALIZED000001/ch_NORMALIZED00000000000001/rcpt_NORMALIZED000000000000000000001",
|
||||||
@@ -105,7 +72,6 @@
|
|||||||
},
|
},
|
||||||
"source_transfer": null,
|
"source_transfer": null,
|
||||||
"statement_descriptor": "Zulip Standard",
|
"statement_descriptor": "Zulip Standard",
|
||||||
"statement_descriptor_suffix": "Zulip Standard",
|
|
||||||
"status": "succeeded",
|
"status": "succeeded",
|
||||||
"transfer_data": null,
|
"transfer_data": null,
|
||||||
"transfer_group": null
|
"transfer_group": null
|
||||||
|
|||||||
@@ -5,19 +5,6 @@
|
|||||||
"application_fee": null,
|
"application_fee": null,
|
||||||
"application_fee_amount": null,
|
"application_fee_amount": null,
|
||||||
"balance_transaction": "txn_NORMALIZED00000000000002",
|
"balance_transaction": "txn_NORMALIZED00000000000002",
|
||||||
"billing_details": {
|
|
||||||
"address": {
|
|
||||||
"city": "Pacific",
|
|
||||||
"country": "United States",
|
|
||||||
"line1": "Under the sea,",
|
|
||||||
"line2": null,
|
|
||||||
"postal_code": "33333",
|
|
||||||
"state": null
|
|
||||||
},
|
|
||||||
"email": null,
|
|
||||||
"name": "Ada Starr",
|
|
||||||
"phone": null
|
|
||||||
},
|
|
||||||
"captured": true,
|
"captured": true,
|
||||||
"created": 1000000000,
|
"created": 1000000000,
|
||||||
"currency": "usd",
|
"currency": "usd",
|
||||||
@@ -45,26 +32,6 @@
|
|||||||
},
|
},
|
||||||
"paid": true,
|
"paid": true,
|
||||||
"payment_intent": null,
|
"payment_intent": null,
|
||||||
"payment_method": "card_NORMALIZED00000000000002",
|
|
||||||
"payment_method_details": {
|
|
||||||
"card": {
|
|
||||||
"brand": "visa",
|
|
||||||
"checks": {
|
|
||||||
"address_line1_check": "pass",
|
|
||||||
"address_postal_code_check": "pass",
|
|
||||||
"cvc_check": "pass"
|
|
||||||
},
|
|
||||||
"country": "US",
|
|
||||||
"exp_month": 3,
|
|
||||||
"exp_year": 2033,
|
|
||||||
"fingerprint": "NORMALIZED000001",
|
|
||||||
"funding": "credit",
|
|
||||||
"last4": "4242",
|
|
||||||
"three_d_secure": null,
|
|
||||||
"wallet": null
|
|
||||||
},
|
|
||||||
"type": "card"
|
|
||||||
},
|
|
||||||
"receipt_email": "hamlet@zulip.com",
|
"receipt_email": "hamlet@zulip.com",
|
||||||
"receipt_number": null,
|
"receipt_number": null,
|
||||||
"receipt_url": "https://pay.stripe.com/receipts/acct_NORMALIZED000001/ch_NORMALIZED00000000000002/rcpt_NORMALIZED000000000000000000002",
|
"receipt_url": "https://pay.stripe.com/receipts/acct_NORMALIZED000001/ch_NORMALIZED00000000000002/rcpt_NORMALIZED000000000000000000002",
|
||||||
@@ -105,7 +72,6 @@
|
|||||||
},
|
},
|
||||||
"source_transfer": null,
|
"source_transfer": null,
|
||||||
"statement_descriptor": "Zulip Standard",
|
"statement_descriptor": "Zulip Standard",
|
||||||
"statement_descriptor_suffix": "Zulip Standard",
|
|
||||||
"status": "succeeded",
|
"status": "succeeded",
|
||||||
"transfer_data": null,
|
"transfer_data": null,
|
||||||
"transfer_group": null
|
"transfer_group": null
|
||||||
|
|||||||
@@ -7,19 +7,6 @@
|
|||||||
"application_fee": null,
|
"application_fee": null,
|
||||||
"application_fee_amount": null,
|
"application_fee_amount": null,
|
||||||
"balance_transaction": "txn_NORMALIZED00000000000001",
|
"balance_transaction": "txn_NORMALIZED00000000000001",
|
||||||
"billing_details": {
|
|
||||||
"address": {
|
|
||||||
"city": "Pacific",
|
|
||||||
"country": "United States",
|
|
||||||
"line1": "Under the sea,",
|
|
||||||
"line2": null,
|
|
||||||
"postal_code": "33333",
|
|
||||||
"state": null
|
|
||||||
},
|
|
||||||
"email": null,
|
|
||||||
"name": "Ada Starr",
|
|
||||||
"phone": null
|
|
||||||
},
|
|
||||||
"captured": true,
|
"captured": true,
|
||||||
"created": 1000000000,
|
"created": 1000000000,
|
||||||
"currency": "usd",
|
"currency": "usd",
|
||||||
@@ -47,26 +34,6 @@
|
|||||||
},
|
},
|
||||||
"paid": true,
|
"paid": true,
|
||||||
"payment_intent": null,
|
"payment_intent": null,
|
||||||
"payment_method": "card_NORMALIZED00000000000001",
|
|
||||||
"payment_method_details": {
|
|
||||||
"card": {
|
|
||||||
"brand": "visa",
|
|
||||||
"checks": {
|
|
||||||
"address_line1_check": "pass",
|
|
||||||
"address_postal_code_check": "pass",
|
|
||||||
"cvc_check": "pass"
|
|
||||||
},
|
|
||||||
"country": "US",
|
|
||||||
"exp_month": 3,
|
|
||||||
"exp_year": 2033,
|
|
||||||
"fingerprint": "NORMALIZED000001",
|
|
||||||
"funding": "credit",
|
|
||||||
"last4": "4242",
|
|
||||||
"three_d_secure": null,
|
|
||||||
"wallet": null
|
|
||||||
},
|
|
||||||
"type": "card"
|
|
||||||
},
|
|
||||||
"receipt_email": "hamlet@zulip.com",
|
"receipt_email": "hamlet@zulip.com",
|
||||||
"receipt_number": null,
|
"receipt_number": null,
|
||||||
"receipt_url": "https://pay.stripe.com/receipts/acct_NORMALIZED000001/ch_NORMALIZED00000000000001/rcpt_NORMALIZED000000000000000000001",
|
"receipt_url": "https://pay.stripe.com/receipts/acct_NORMALIZED000001/ch_NORMALIZED00000000000001/rcpt_NORMALIZED000000000000000000001",
|
||||||
@@ -101,7 +68,6 @@
|
|||||||
},
|
},
|
||||||
"source_transfer": null,
|
"source_transfer": null,
|
||||||
"statement_descriptor": "Zulip Standard",
|
"statement_descriptor": "Zulip Standard",
|
||||||
"statement_descriptor_suffix": "Zulip Standard",
|
|
||||||
"status": "succeeded",
|
"status": "succeeded",
|
||||||
"transfer_data": null,
|
"transfer_data": null,
|
||||||
"transfer_group": null
|
"transfer_group": null
|
||||||
|
|||||||
@@ -7,19 +7,6 @@
|
|||||||
"application_fee": null,
|
"application_fee": null,
|
||||||
"application_fee_amount": null,
|
"application_fee_amount": null,
|
||||||
"balance_transaction": "txn_NORMALIZED00000000000002",
|
"balance_transaction": "txn_NORMALIZED00000000000002",
|
||||||
"billing_details": {
|
|
||||||
"address": {
|
|
||||||
"city": "Pacific",
|
|
||||||
"country": "United States",
|
|
||||||
"line1": "Under the sea,",
|
|
||||||
"line2": null,
|
|
||||||
"postal_code": "33333",
|
|
||||||
"state": null
|
|
||||||
},
|
|
||||||
"email": null,
|
|
||||||
"name": "Ada Starr",
|
|
||||||
"phone": null
|
|
||||||
},
|
|
||||||
"captured": true,
|
"captured": true,
|
||||||
"created": 1000000000,
|
"created": 1000000000,
|
||||||
"currency": "usd",
|
"currency": "usd",
|
||||||
@@ -47,26 +34,6 @@
|
|||||||
},
|
},
|
||||||
"paid": true,
|
"paid": true,
|
||||||
"payment_intent": null,
|
"payment_intent": null,
|
||||||
"payment_method": "card_NORMALIZED00000000000002",
|
|
||||||
"payment_method_details": {
|
|
||||||
"card": {
|
|
||||||
"brand": "visa",
|
|
||||||
"checks": {
|
|
||||||
"address_line1_check": "pass",
|
|
||||||
"address_postal_code_check": "pass",
|
|
||||||
"cvc_check": "pass"
|
|
||||||
},
|
|
||||||
"country": "US",
|
|
||||||
"exp_month": 3,
|
|
||||||
"exp_year": 2033,
|
|
||||||
"fingerprint": "NORMALIZED000001",
|
|
||||||
"funding": "credit",
|
|
||||||
"last4": "4242",
|
|
||||||
"three_d_secure": null,
|
|
||||||
"wallet": null
|
|
||||||
},
|
|
||||||
"type": "card"
|
|
||||||
},
|
|
||||||
"receipt_email": "hamlet@zulip.com",
|
"receipt_email": "hamlet@zulip.com",
|
||||||
"receipt_number": null,
|
"receipt_number": null,
|
||||||
"receipt_url": "https://pay.stripe.com/receipts/acct_NORMALIZED000001/ch_NORMALIZED00000000000002/rcpt_NORMALIZED000000000000000000002",
|
"receipt_url": "https://pay.stripe.com/receipts/acct_NORMALIZED000001/ch_NORMALIZED00000000000002/rcpt_NORMALIZED000000000000000000002",
|
||||||
@@ -101,7 +68,6 @@
|
|||||||
},
|
},
|
||||||
"source_transfer": null,
|
"source_transfer": null,
|
||||||
"statement_descriptor": "Zulip Standard",
|
"statement_descriptor": "Zulip Standard",
|
||||||
"statement_descriptor_suffix": "Zulip Standard",
|
|
||||||
"status": "succeeded",
|
"status": "succeeded",
|
||||||
"transfer_data": null,
|
"transfer_data": null,
|
||||||
"transfer_group": null
|
"transfer_group": null
|
||||||
@@ -113,19 +79,6 @@
|
|||||||
"application_fee": null,
|
"application_fee": null,
|
||||||
"application_fee_amount": null,
|
"application_fee_amount": null,
|
||||||
"balance_transaction": "txn_NORMALIZED00000000000001",
|
"balance_transaction": "txn_NORMALIZED00000000000001",
|
||||||
"billing_details": {
|
|
||||||
"address": {
|
|
||||||
"city": "Pacific",
|
|
||||||
"country": "United States",
|
|
||||||
"line1": "Under the sea,",
|
|
||||||
"line2": null,
|
|
||||||
"postal_code": "33333",
|
|
||||||
"state": null
|
|
||||||
},
|
|
||||||
"email": null,
|
|
||||||
"name": "Ada Starr",
|
|
||||||
"phone": null
|
|
||||||
},
|
|
||||||
"captured": true,
|
"captured": true,
|
||||||
"created": 1000000000,
|
"created": 1000000000,
|
||||||
"currency": "usd",
|
"currency": "usd",
|
||||||
@@ -153,26 +106,6 @@
|
|||||||
},
|
},
|
||||||
"paid": true,
|
"paid": true,
|
||||||
"payment_intent": null,
|
"payment_intent": null,
|
||||||
"payment_method": "card_NORMALIZED00000000000001",
|
|
||||||
"payment_method_details": {
|
|
||||||
"card": {
|
|
||||||
"brand": "visa",
|
|
||||||
"checks": {
|
|
||||||
"address_line1_check": "pass",
|
|
||||||
"address_postal_code_check": "pass",
|
|
||||||
"cvc_check": "pass"
|
|
||||||
},
|
|
||||||
"country": "US",
|
|
||||||
"exp_month": 3,
|
|
||||||
"exp_year": 2033,
|
|
||||||
"fingerprint": "NORMALIZED000001",
|
|
||||||
"funding": "credit",
|
|
||||||
"last4": "4242",
|
|
||||||
"three_d_secure": null,
|
|
||||||
"wallet": null
|
|
||||||
},
|
|
||||||
"type": "card"
|
|
||||||
},
|
|
||||||
"receipt_email": "hamlet@zulip.com",
|
"receipt_email": "hamlet@zulip.com",
|
||||||
"receipt_number": null,
|
"receipt_number": null,
|
||||||
"receipt_url": "https://pay.stripe.com/receipts/acct_NORMALIZED000001/ch_NORMALIZED00000000000001/rcpt_NORMALIZED000000000000000000001",
|
"receipt_url": "https://pay.stripe.com/receipts/acct_NORMALIZED000001/ch_NORMALIZED00000000000001/rcpt_NORMALIZED000000000000000000001",
|
||||||
@@ -207,7 +140,6 @@
|
|||||||
},
|
},
|
||||||
"source_transfer": null,
|
"source_transfer": null,
|
||||||
"statement_descriptor": "Zulip Standard",
|
"statement_descriptor": "Zulip Standard",
|
||||||
"statement_descriptor_suffix": "Zulip Standard",
|
|
||||||
"status": "succeeded",
|
"status": "succeeded",
|
||||||
"transfer_data": null,
|
"transfer_data": null,
|
||||||
"transfer_group": null
|
"transfer_group": null
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
{
|
{
|
||||||
"account_balance": 0,
|
"account_balance": 0,
|
||||||
"address": null,
|
|
||||||
"balance": 0,
|
|
||||||
"created": 1000000000,
|
"created": 1000000000,
|
||||||
"currency": null,
|
"currency": null,
|
||||||
"default_source": "card_NORMALIZED00000000000001",
|
"default_source": "card_NORMALIZED00000000000001",
|
||||||
@@ -13,7 +11,6 @@
|
|||||||
"invoice_prefix": "NORMA01",
|
"invoice_prefix": "NORMA01",
|
||||||
"invoice_settings": {
|
"invoice_settings": {
|
||||||
"custom_fields": null,
|
"custom_fields": null,
|
||||||
"default_payment_method": null,
|
|
||||||
"footer": null
|
"footer": null
|
||||||
},
|
},
|
||||||
"livemode": false,
|
"livemode": false,
|
||||||
@@ -21,10 +18,7 @@
|
|||||||
"realm_id": "1",
|
"realm_id": "1",
|
||||||
"realm_str": "zulip"
|
"realm_str": "zulip"
|
||||||
},
|
},
|
||||||
"name": null,
|
|
||||||
"object": "customer",
|
"object": "customer",
|
||||||
"phone": null,
|
|
||||||
"preferred_locales": [],
|
|
||||||
"shipping": null,
|
"shipping": null,
|
||||||
"sources": {
|
"sources": {
|
||||||
"data": [
|
"data": [
|
||||||
@@ -66,14 +60,6 @@
|
|||||||
"total_count": 0,
|
"total_count": 0,
|
||||||
"url": "/v1/customers/cus_NORMALIZED0001/subscriptions"
|
"url": "/v1/customers/cus_NORMALIZED0001/subscriptions"
|
||||||
},
|
},
|
||||||
"tax_exempt": "none",
|
|
||||||
"tax_ids": {
|
|
||||||
"data": [],
|
|
||||||
"has_more": false,
|
|
||||||
"object": "list",
|
|
||||||
"total_count": 0,
|
|
||||||
"url": "/v1/customers/cus_NORMALIZED0001/tax_ids"
|
|
||||||
},
|
|
||||||
"tax_info": null,
|
"tax_info": null,
|
||||||
"tax_info_verification": null
|
"tax_info_verification": null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
{
|
{
|
||||||
"account_balance": 0,
|
"account_balance": 0,
|
||||||
"address": null,
|
|
||||||
"balance": 0,
|
|
||||||
"created": 1000000000,
|
"created": 1000000000,
|
||||||
"currency": "usd",
|
"currency": "usd",
|
||||||
"default_source": {
|
"default_source": {
|
||||||
@@ -37,7 +35,6 @@
|
|||||||
"invoice_prefix": "NORMA01",
|
"invoice_prefix": "NORMA01",
|
||||||
"invoice_settings": {
|
"invoice_settings": {
|
||||||
"custom_fields": null,
|
"custom_fields": null,
|
||||||
"default_payment_method": null,
|
|
||||||
"footer": null
|
"footer": null
|
||||||
},
|
},
|
||||||
"livemode": false,
|
"livemode": false,
|
||||||
@@ -45,10 +42,7 @@
|
|||||||
"realm_id": "1",
|
"realm_id": "1",
|
||||||
"realm_str": "zulip"
|
"realm_str": "zulip"
|
||||||
},
|
},
|
||||||
"name": null,
|
|
||||||
"object": "customer",
|
"object": "customer",
|
||||||
"phone": null,
|
|
||||||
"preferred_locales": [],
|
|
||||||
"shipping": null,
|
"shipping": null,
|
||||||
"sources": {
|
"sources": {
|
||||||
"data": [
|
"data": [
|
||||||
@@ -90,14 +84,6 @@
|
|||||||
"total_count": 0,
|
"total_count": 0,
|
||||||
"url": "/v1/customers/cus_NORMALIZED0001/subscriptions"
|
"url": "/v1/customers/cus_NORMALIZED0001/subscriptions"
|
||||||
},
|
},
|
||||||
"tax_exempt": "none",
|
|
||||||
"tax_ids": {
|
|
||||||
"data": [],
|
|
||||||
"has_more": false,
|
|
||||||
"object": "list",
|
|
||||||
"total_count": 0,
|
|
||||||
"url": "/v1/customers/cus_NORMALIZED0001/tax_ids"
|
|
||||||
},
|
|
||||||
"tax_info": null,
|
"tax_info": null,
|
||||||
"tax_info_verification": null
|
"tax_info_verification": null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
{
|
{
|
||||||
"account_balance": 0,
|
"account_balance": 0,
|
||||||
"address": null,
|
|
||||||
"balance": 0,
|
|
||||||
"created": 1000000000,
|
"created": 1000000000,
|
||||||
"currency": "usd",
|
"currency": "usd",
|
||||||
"default_source": "card_NORMALIZED00000000000002",
|
"default_source": "card_NORMALIZED00000000000002",
|
||||||
@@ -13,7 +11,6 @@
|
|||||||
"invoice_prefix": "NORMA01",
|
"invoice_prefix": "NORMA01",
|
||||||
"invoice_settings": {
|
"invoice_settings": {
|
||||||
"custom_fields": null,
|
"custom_fields": null,
|
||||||
"default_payment_method": null,
|
|
||||||
"footer": null
|
"footer": null
|
||||||
},
|
},
|
||||||
"livemode": false,
|
"livemode": false,
|
||||||
@@ -21,10 +18,7 @@
|
|||||||
"realm_id": "1",
|
"realm_id": "1",
|
||||||
"realm_str": "zulip"
|
"realm_str": "zulip"
|
||||||
},
|
},
|
||||||
"name": null,
|
|
||||||
"object": "customer",
|
"object": "customer",
|
||||||
"phone": null,
|
|
||||||
"preferred_locales": [],
|
|
||||||
"shipping": null,
|
"shipping": null,
|
||||||
"sources": {
|
"sources": {
|
||||||
"data": [
|
"data": [
|
||||||
@@ -66,14 +60,6 @@
|
|||||||
"total_count": 0,
|
"total_count": 0,
|
||||||
"url": "/v1/customers/cus_NORMALIZED0001/subscriptions"
|
"url": "/v1/customers/cus_NORMALIZED0001/subscriptions"
|
||||||
},
|
},
|
||||||
"tax_exempt": "none",
|
|
||||||
"tax_ids": {
|
|
||||||
"data": [],
|
|
||||||
"has_more": false,
|
|
||||||
"object": "list",
|
|
||||||
"total_count": 0,
|
|
||||||
"url": "/v1/customers/cus_NORMALIZED0001/tax_ids"
|
|
||||||
},
|
|
||||||
"tax_info": null,
|
"tax_info": null,
|
||||||
"tax_info_verification": null
|
"tax_info_verification": null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,35 +1,24 @@
|
|||||||
{
|
{
|
||||||
"account_country": "US",
|
|
||||||
"account_name": "Dev account",
|
|
||||||
"amount_due": 0,
|
"amount_due": 0,
|
||||||
"amount_paid": 0,
|
"amount_paid": 0,
|
||||||
"amount_remaining": 0,
|
"amount_remaining": 0,
|
||||||
"application_fee_amount": null,
|
"application_fee": null,
|
||||||
"attempt_count": 0,
|
"attempt_count": 0,
|
||||||
"attempted": false,
|
"attempted": false,
|
||||||
"auto_advance": true,
|
"auto_advance": true,
|
||||||
"billing": "charge_automatically",
|
"billing": "charge_automatically",
|
||||||
"billing_reason": "manual",
|
"billing_reason": "manual",
|
||||||
"charge": null,
|
"charge": null,
|
||||||
"collection_method": "charge_automatically",
|
|
||||||
"created": 1000000000,
|
|
||||||
"currency": "usd",
|
"currency": "usd",
|
||||||
"custom_fields": null,
|
"custom_fields": null,
|
||||||
"customer": "cus_NORMALIZED0001",
|
"customer": "cus_NORMALIZED0001",
|
||||||
"customer_address": null,
|
"date": 1000000000,
|
||||||
"customer_email": "hamlet@zulip.com",
|
|
||||||
"customer_name": null,
|
|
||||||
"customer_phone": null,
|
|
||||||
"customer_shipping": null,
|
|
||||||
"customer_tax_exempt": "none",
|
|
||||||
"customer_tax_ids": [],
|
|
||||||
"default_payment_method": null,
|
|
||||||
"default_source": null,
|
"default_source": null,
|
||||||
"default_tax_rates": [],
|
|
||||||
"description": "",
|
"description": "",
|
||||||
"discount": null,
|
"discount": null,
|
||||||
"due_date": null,
|
"due_date": null,
|
||||||
"ending_balance": null,
|
"ending_balance": null,
|
||||||
|
"finalized_at": null,
|
||||||
"footer": null,
|
"footer": null,
|
||||||
"hosted_invoice_url": null,
|
"hosted_invoice_url": null,
|
||||||
"id": "in_NORMALIZED00000000000001",
|
"id": "in_NORMALIZED00000000000001",
|
||||||
@@ -54,8 +43,6 @@
|
|||||||
"proration": false,
|
"proration": false,
|
||||||
"quantity": 6,
|
"quantity": 6,
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"tax_amounts": [],
|
|
||||||
"tax_rates": [],
|
|
||||||
"type": "invoiceitem"
|
"type": "invoiceitem"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -76,8 +63,6 @@
|
|||||||
"proration": false,
|
"proration": false,
|
||||||
"quantity": 1,
|
"quantity": 1,
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"tax_amounts": [],
|
|
||||||
"tax_rates": [],
|
|
||||||
"type": "invoiceitem"
|
"type": "invoiceitem"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -92,26 +77,16 @@
|
|||||||
"number": "NORMALI-0001",
|
"number": "NORMALI-0001",
|
||||||
"object": "invoice",
|
"object": "invoice",
|
||||||
"paid": false,
|
"paid": false,
|
||||||
"payment_intent": null,
|
|
||||||
"period_end": 1000000000,
|
"period_end": 1000000000,
|
||||||
"period_start": 1000000000,
|
"period_start": 1000000000,
|
||||||
"post_payment_credit_notes_amount": 0,
|
|
||||||
"pre_payment_credit_notes_amount": 0,
|
|
||||||
"receipt_number": null,
|
"receipt_number": null,
|
||||||
"starting_balance": 0,
|
"starting_balance": 0,
|
||||||
"statement_descriptor": "Zulip Standard",
|
"statement_descriptor": "Zulip Standard",
|
||||||
"status": "draft",
|
"status": "draft",
|
||||||
"status_transitions": {
|
|
||||||
"finalized_at": null,
|
|
||||||
"marked_uncollectible_at": null,
|
|
||||||
"paid_at": null,
|
|
||||||
"voided_at": null
|
|
||||||
},
|
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"subtotal": 0,
|
"subtotal": 0,
|
||||||
"tax": null,
|
"tax": 0,
|
||||||
"tax_percent": null,
|
"tax_percent": null,
|
||||||
"total": 0,
|
"total": 0,
|
||||||
"total_tax_amounts": [],
|
|
||||||
"webhooks_delivered_at": null
|
"webhooks_delivered_at": null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,35 +1,24 @@
|
|||||||
{
|
{
|
||||||
"account_country": "US",
|
|
||||||
"account_name": "Dev account",
|
|
||||||
"amount_due": 0,
|
"amount_due": 0,
|
||||||
"amount_paid": 0,
|
"amount_paid": 0,
|
||||||
"amount_remaining": 0,
|
"amount_remaining": 0,
|
||||||
"application_fee_amount": null,
|
"application_fee": null,
|
||||||
"attempt_count": 0,
|
"attempt_count": 0,
|
||||||
"attempted": false,
|
"attempted": false,
|
||||||
"auto_advance": true,
|
"auto_advance": true,
|
||||||
"billing": "charge_automatically",
|
"billing": "charge_automatically",
|
||||||
"billing_reason": "manual",
|
"billing_reason": "manual",
|
||||||
"charge": null,
|
"charge": null,
|
||||||
"collection_method": "charge_automatically",
|
|
||||||
"created": 1000000000,
|
|
||||||
"currency": "usd",
|
"currency": "usd",
|
||||||
"custom_fields": null,
|
"custom_fields": null,
|
||||||
"customer": "cus_NORMALIZED0001",
|
"customer": "cus_NORMALIZED0001",
|
||||||
"customer_address": null,
|
"date": 1000000000,
|
||||||
"customer_email": "hamlet@zulip.com",
|
|
||||||
"customer_name": null,
|
|
||||||
"customer_phone": null,
|
|
||||||
"customer_shipping": null,
|
|
||||||
"customer_tax_exempt": "none",
|
|
||||||
"customer_tax_ids": [],
|
|
||||||
"default_payment_method": null,
|
|
||||||
"default_source": null,
|
"default_source": null,
|
||||||
"default_tax_rates": [],
|
|
||||||
"description": "",
|
"description": "",
|
||||||
"discount": null,
|
"discount": null,
|
||||||
"due_date": null,
|
"due_date": null,
|
||||||
"ending_balance": null,
|
"ending_balance": null,
|
||||||
|
"finalized_at": null,
|
||||||
"footer": null,
|
"footer": null,
|
||||||
"hosted_invoice_url": null,
|
"hosted_invoice_url": null,
|
||||||
"id": "in_NORMALIZED00000000000002",
|
"id": "in_NORMALIZED00000000000002",
|
||||||
@@ -54,8 +43,6 @@
|
|||||||
"proration": false,
|
"proration": false,
|
||||||
"quantity": 6,
|
"quantity": 6,
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"tax_amounts": [],
|
|
||||||
"tax_rates": [],
|
|
||||||
"type": "invoiceitem"
|
"type": "invoiceitem"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -76,8 +63,6 @@
|
|||||||
"proration": false,
|
"proration": false,
|
||||||
"quantity": 1,
|
"quantity": 1,
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"tax_amounts": [],
|
|
||||||
"tax_rates": [],
|
|
||||||
"type": "invoiceitem"
|
"type": "invoiceitem"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -92,26 +77,16 @@
|
|||||||
"number": "NORMALI-0002",
|
"number": "NORMALI-0002",
|
||||||
"object": "invoice",
|
"object": "invoice",
|
||||||
"paid": false,
|
"paid": false,
|
||||||
"payment_intent": null,
|
|
||||||
"period_end": 1000000000,
|
"period_end": 1000000000,
|
||||||
"period_start": 1000000000,
|
"period_start": 1000000000,
|
||||||
"post_payment_credit_notes_amount": 0,
|
|
||||||
"pre_payment_credit_notes_amount": 0,
|
|
||||||
"receipt_number": null,
|
"receipt_number": null,
|
||||||
"starting_balance": 0,
|
"starting_balance": 0,
|
||||||
"statement_descriptor": "Zulip Standard",
|
"statement_descriptor": "Zulip Standard",
|
||||||
"status": "draft",
|
"status": "draft",
|
||||||
"status_transitions": {
|
|
||||||
"finalized_at": null,
|
|
||||||
"marked_uncollectible_at": null,
|
|
||||||
"paid_at": null,
|
|
||||||
"voided_at": null
|
|
||||||
},
|
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"subtotal": 0,
|
"subtotal": 0,
|
||||||
"tax": null,
|
"tax": 0,
|
||||||
"tax_percent": null,
|
"tax_percent": null,
|
||||||
"total": 0,
|
"total": 0,
|
||||||
"total_tax_amounts": [],
|
|
||||||
"webhooks_delivered_at": null
|
"webhooks_delivered_at": null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,35 +1,24 @@
|
|||||||
{
|
{
|
||||||
"account_country": "US",
|
|
||||||
"account_name": "Dev account",
|
|
||||||
"amount_due": 0,
|
"amount_due": 0,
|
||||||
"amount_paid": 0,
|
"amount_paid": 0,
|
||||||
"amount_remaining": 0,
|
"amount_remaining": 0,
|
||||||
"application_fee_amount": null,
|
"application_fee": null,
|
||||||
"attempt_count": 0,
|
"attempt_count": 0,
|
||||||
"attempted": true,
|
"attempted": true,
|
||||||
"auto_advance": false,
|
"auto_advance": false,
|
||||||
"billing": "charge_automatically",
|
"billing": "charge_automatically",
|
||||||
"billing_reason": "manual",
|
"billing_reason": "manual",
|
||||||
"charge": null,
|
"charge": null,
|
||||||
"collection_method": "charge_automatically",
|
|
||||||
"created": 1000000000,
|
|
||||||
"currency": "usd",
|
"currency": "usd",
|
||||||
"custom_fields": null,
|
"custom_fields": null,
|
||||||
"customer": "cus_NORMALIZED0001",
|
"customer": "cus_NORMALIZED0001",
|
||||||
"customer_address": null,
|
"date": 1000000000,
|
||||||
"customer_email": "hamlet@zulip.com",
|
|
||||||
"customer_name": null,
|
|
||||||
"customer_phone": null,
|
|
||||||
"customer_shipping": null,
|
|
||||||
"customer_tax_exempt": "none",
|
|
||||||
"customer_tax_ids": [],
|
|
||||||
"default_payment_method": null,
|
|
||||||
"default_source": null,
|
"default_source": null,
|
||||||
"default_tax_rates": [],
|
|
||||||
"description": "",
|
"description": "",
|
||||||
"discount": null,
|
"discount": null,
|
||||||
"due_date": 1000000000,
|
"due_date": 1000000000,
|
||||||
"ending_balance": 0,
|
"ending_balance": 0,
|
||||||
|
"finalized_at": 1000000000,
|
||||||
"footer": null,
|
"footer": null,
|
||||||
"hosted_invoice_url": "https://pay.stripe.com/invoice/invst_NORMALIZED0000000000000001",
|
"hosted_invoice_url": "https://pay.stripe.com/invoice/invst_NORMALIZED0000000000000001",
|
||||||
"id": "in_NORMALIZED00000000000001",
|
"id": "in_NORMALIZED00000000000001",
|
||||||
@@ -54,8 +43,6 @@
|
|||||||
"proration": false,
|
"proration": false,
|
||||||
"quantity": 6,
|
"quantity": 6,
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"tax_amounts": [],
|
|
||||||
"tax_rates": [],
|
|
||||||
"type": "invoiceitem"
|
"type": "invoiceitem"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -76,8 +63,6 @@
|
|||||||
"proration": false,
|
"proration": false,
|
||||||
"quantity": 1,
|
"quantity": 1,
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"tax_amounts": [],
|
|
||||||
"tax_rates": [],
|
|
||||||
"type": "invoiceitem"
|
"type": "invoiceitem"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -92,26 +77,16 @@
|
|||||||
"number": "NORMALI-0001",
|
"number": "NORMALI-0001",
|
||||||
"object": "invoice",
|
"object": "invoice",
|
||||||
"paid": true,
|
"paid": true,
|
||||||
"payment_intent": null,
|
|
||||||
"period_end": 1000000000,
|
"period_end": 1000000000,
|
||||||
"period_start": 1000000000,
|
"period_start": 1000000000,
|
||||||
"post_payment_credit_notes_amount": 0,
|
|
||||||
"pre_payment_credit_notes_amount": 0,
|
|
||||||
"receipt_number": null,
|
"receipt_number": null,
|
||||||
"starting_balance": 0,
|
"starting_balance": 0,
|
||||||
"statement_descriptor": "Zulip Standard",
|
"statement_descriptor": "Zulip Standard",
|
||||||
"status": "paid",
|
"status": "paid",
|
||||||
"status_transitions": {
|
|
||||||
"finalized_at": 1000000000,
|
|
||||||
"marked_uncollectible_at": null,
|
|
||||||
"paid_at": 1000000000,
|
|
||||||
"voided_at": null
|
|
||||||
},
|
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"subtotal": 0,
|
"subtotal": 0,
|
||||||
"tax": null,
|
"tax": 0,
|
||||||
"tax_percent": null,
|
"tax_percent": null,
|
||||||
"total": 0,
|
"total": 0,
|
||||||
"total_tax_amounts": [],
|
|
||||||
"webhooks_delivered_at": 1000000000
|
"webhooks_delivered_at": 1000000000
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,35 +1,24 @@
|
|||||||
{
|
{
|
||||||
"account_country": "US",
|
|
||||||
"account_name": "Dev account",
|
|
||||||
"amount_due": 0,
|
"amount_due": 0,
|
||||||
"amount_paid": 0,
|
"amount_paid": 0,
|
||||||
"amount_remaining": 0,
|
"amount_remaining": 0,
|
||||||
"application_fee_amount": null,
|
"application_fee": null,
|
||||||
"attempt_count": 0,
|
"attempt_count": 0,
|
||||||
"attempted": true,
|
"attempted": true,
|
||||||
"auto_advance": false,
|
"auto_advance": false,
|
||||||
"billing": "charge_automatically",
|
"billing": "charge_automatically",
|
||||||
"billing_reason": "manual",
|
"billing_reason": "manual",
|
||||||
"charge": null,
|
"charge": null,
|
||||||
"collection_method": "charge_automatically",
|
|
||||||
"created": 1000000000,
|
|
||||||
"currency": "usd",
|
"currency": "usd",
|
||||||
"custom_fields": null,
|
"custom_fields": null,
|
||||||
"customer": "cus_NORMALIZED0001",
|
"customer": "cus_NORMALIZED0001",
|
||||||
"customer_address": null,
|
"date": 1000000000,
|
||||||
"customer_email": "hamlet@zulip.com",
|
|
||||||
"customer_name": null,
|
|
||||||
"customer_phone": null,
|
|
||||||
"customer_shipping": null,
|
|
||||||
"customer_tax_exempt": "none",
|
|
||||||
"customer_tax_ids": [],
|
|
||||||
"default_payment_method": null,
|
|
||||||
"default_source": null,
|
"default_source": null,
|
||||||
"default_tax_rates": [],
|
|
||||||
"description": "",
|
"description": "",
|
||||||
"discount": null,
|
"discount": null,
|
||||||
"due_date": 1000000000,
|
"due_date": 1000000000,
|
||||||
"ending_balance": 0,
|
"ending_balance": 0,
|
||||||
|
"finalized_at": 1000000000,
|
||||||
"footer": null,
|
"footer": null,
|
||||||
"hosted_invoice_url": "https://pay.stripe.com/invoice/invst_NORMALIZED0000000000000002",
|
"hosted_invoice_url": "https://pay.stripe.com/invoice/invst_NORMALIZED0000000000000002",
|
||||||
"id": "in_NORMALIZED00000000000002",
|
"id": "in_NORMALIZED00000000000002",
|
||||||
@@ -54,8 +43,6 @@
|
|||||||
"proration": false,
|
"proration": false,
|
||||||
"quantity": 6,
|
"quantity": 6,
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"tax_amounts": [],
|
|
||||||
"tax_rates": [],
|
|
||||||
"type": "invoiceitem"
|
"type": "invoiceitem"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -76,8 +63,6 @@
|
|||||||
"proration": false,
|
"proration": false,
|
||||||
"quantity": 1,
|
"quantity": 1,
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"tax_amounts": [],
|
|
||||||
"tax_rates": [],
|
|
||||||
"type": "invoiceitem"
|
"type": "invoiceitem"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -92,26 +77,16 @@
|
|||||||
"number": "NORMALI-0002",
|
"number": "NORMALI-0002",
|
||||||
"object": "invoice",
|
"object": "invoice",
|
||||||
"paid": true,
|
"paid": true,
|
||||||
"payment_intent": null,
|
|
||||||
"period_end": 1000000000,
|
"period_end": 1000000000,
|
||||||
"period_start": 1000000000,
|
"period_start": 1000000000,
|
||||||
"post_payment_credit_notes_amount": 0,
|
|
||||||
"pre_payment_credit_notes_amount": 0,
|
|
||||||
"receipt_number": null,
|
"receipt_number": null,
|
||||||
"starting_balance": 0,
|
"starting_balance": 0,
|
||||||
"statement_descriptor": "Zulip Standard",
|
"statement_descriptor": "Zulip Standard",
|
||||||
"status": "paid",
|
"status": "paid",
|
||||||
"status_transitions": {
|
|
||||||
"finalized_at": 1000000000,
|
|
||||||
"marked_uncollectible_at": null,
|
|
||||||
"paid_at": 1000000000,
|
|
||||||
"voided_at": null
|
|
||||||
},
|
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"subtotal": 0,
|
"subtotal": 0,
|
||||||
"tax": null,
|
"tax": 0,
|
||||||
"tax_percent": null,
|
"tax_percent": null,
|
||||||
"total": 0,
|
"total": 0,
|
||||||
"total_tax_amounts": [],
|
|
||||||
"webhooks_delivered_at": 1000000000
|
"webhooks_delivered_at": 1000000000
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,37 +1,26 @@
|
|||||||
{
|
{
|
||||||
"data": [
|
"data": [
|
||||||
{
|
{
|
||||||
"account_country": "US",
|
|
||||||
"account_name": "Dev account",
|
|
||||||
"amount_due": 0,
|
"amount_due": 0,
|
||||||
"amount_paid": 0,
|
"amount_paid": 0,
|
||||||
"amount_remaining": 0,
|
"amount_remaining": 0,
|
||||||
"application_fee_amount": null,
|
"application_fee": null,
|
||||||
"attempt_count": 0,
|
"attempt_count": 0,
|
||||||
"attempted": true,
|
"attempted": true,
|
||||||
"auto_advance": false,
|
"auto_advance": false,
|
||||||
"billing": "charge_automatically",
|
"billing": "charge_automatically",
|
||||||
"billing_reason": "manual",
|
"billing_reason": "manual",
|
||||||
"charge": null,
|
"charge": null,
|
||||||
"collection_method": "charge_automatically",
|
|
||||||
"created": 1000000000,
|
|
||||||
"currency": "usd",
|
"currency": "usd",
|
||||||
"custom_fields": null,
|
"custom_fields": null,
|
||||||
"customer": "cus_NORMALIZED0001",
|
"customer": "cus_NORMALIZED0001",
|
||||||
"customer_address": null,
|
"date": 1000000000,
|
||||||
"customer_email": "hamlet@zulip.com",
|
|
||||||
"customer_name": null,
|
|
||||||
"customer_phone": null,
|
|
||||||
"customer_shipping": null,
|
|
||||||
"customer_tax_exempt": "none",
|
|
||||||
"customer_tax_ids": [],
|
|
||||||
"default_payment_method": null,
|
|
||||||
"default_source": null,
|
"default_source": null,
|
||||||
"default_tax_rates": [],
|
|
||||||
"description": "",
|
"description": "",
|
||||||
"discount": null,
|
"discount": null,
|
||||||
"due_date": 1000000000,
|
"due_date": 1000000000,
|
||||||
"ending_balance": 0,
|
"ending_balance": 0,
|
||||||
|
"finalized_at": 1000000000,
|
||||||
"footer": null,
|
"footer": null,
|
||||||
"hosted_invoice_url": "https://pay.stripe.com/invoice/invst_NORMALIZED0000000000000001",
|
"hosted_invoice_url": "https://pay.stripe.com/invoice/invst_NORMALIZED0000000000000001",
|
||||||
"id": "in_NORMALIZED00000000000001",
|
"id": "in_NORMALIZED00000000000001",
|
||||||
@@ -56,8 +45,6 @@
|
|||||||
"proration": false,
|
"proration": false,
|
||||||
"quantity": 6,
|
"quantity": 6,
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"tax_amounts": [],
|
|
||||||
"tax_rates": [],
|
|
||||||
"type": "invoiceitem"
|
"type": "invoiceitem"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -78,8 +65,6 @@
|
|||||||
"proration": false,
|
"proration": false,
|
||||||
"quantity": 1,
|
"quantity": 1,
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"tax_amounts": [],
|
|
||||||
"tax_rates": [],
|
|
||||||
"type": "invoiceitem"
|
"type": "invoiceitem"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -94,27 +79,17 @@
|
|||||||
"number": "NORMALI-0001",
|
"number": "NORMALI-0001",
|
||||||
"object": "invoice",
|
"object": "invoice",
|
||||||
"paid": true,
|
"paid": true,
|
||||||
"payment_intent": null,
|
|
||||||
"period_end": 1000000000,
|
"period_end": 1000000000,
|
||||||
"period_start": 1000000000,
|
"period_start": 1000000000,
|
||||||
"post_payment_credit_notes_amount": 0,
|
|
||||||
"pre_payment_credit_notes_amount": 0,
|
|
||||||
"receipt_number": null,
|
"receipt_number": null,
|
||||||
"starting_balance": 0,
|
"starting_balance": 0,
|
||||||
"statement_descriptor": "Zulip Standard",
|
"statement_descriptor": "Zulip Standard",
|
||||||
"status": "paid",
|
"status": "paid",
|
||||||
"status_transitions": {
|
|
||||||
"finalized_at": 1000000000,
|
|
||||||
"marked_uncollectible_at": null,
|
|
||||||
"paid_at": 1000000000,
|
|
||||||
"voided_at": null
|
|
||||||
},
|
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"subtotal": 0,
|
"subtotal": 0,
|
||||||
"tax": null,
|
"tax": 0,
|
||||||
"tax_percent": null,
|
"tax_percent": null,
|
||||||
"total": 0,
|
"total": 0,
|
||||||
"total_tax_amounts": [],
|
|
||||||
"webhooks_delivered_at": 1000000000
|
"webhooks_delivered_at": 1000000000
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,37 +1,26 @@
|
|||||||
{
|
{
|
||||||
"data": [
|
"data": [
|
||||||
{
|
{
|
||||||
"account_country": "US",
|
|
||||||
"account_name": "Dev account",
|
|
||||||
"amount_due": 0,
|
"amount_due": 0,
|
||||||
"amount_paid": 0,
|
"amount_paid": 0,
|
||||||
"amount_remaining": 0,
|
"amount_remaining": 0,
|
||||||
"application_fee_amount": null,
|
"application_fee": null,
|
||||||
"attempt_count": 0,
|
"attempt_count": 0,
|
||||||
"attempted": true,
|
"attempted": true,
|
||||||
"auto_advance": false,
|
"auto_advance": false,
|
||||||
"billing": "charge_automatically",
|
"billing": "charge_automatically",
|
||||||
"billing_reason": "manual",
|
"billing_reason": "manual",
|
||||||
"charge": null,
|
"charge": null,
|
||||||
"collection_method": "charge_automatically",
|
|
||||||
"created": 1000000000,
|
|
||||||
"currency": "usd",
|
"currency": "usd",
|
||||||
"custom_fields": null,
|
"custom_fields": null,
|
||||||
"customer": "cus_NORMALIZED0001",
|
"customer": "cus_NORMALIZED0001",
|
||||||
"customer_address": null,
|
"date": 1000000000,
|
||||||
"customer_email": "hamlet@zulip.com",
|
|
||||||
"customer_name": null,
|
|
||||||
"customer_phone": null,
|
|
||||||
"customer_shipping": null,
|
|
||||||
"customer_tax_exempt": "none",
|
|
||||||
"customer_tax_ids": [],
|
|
||||||
"default_payment_method": null,
|
|
||||||
"default_source": null,
|
"default_source": null,
|
||||||
"default_tax_rates": [],
|
|
||||||
"description": "",
|
"description": "",
|
||||||
"discount": null,
|
"discount": null,
|
||||||
"due_date": 1000000000,
|
"due_date": 1000000000,
|
||||||
"ending_balance": 0,
|
"ending_balance": 0,
|
||||||
|
"finalized_at": 1000000000,
|
||||||
"footer": null,
|
"footer": null,
|
||||||
"hosted_invoice_url": "https://pay.stripe.com/invoice/invst_NORMALIZED0000000000000002",
|
"hosted_invoice_url": "https://pay.stripe.com/invoice/invst_NORMALIZED0000000000000002",
|
||||||
"id": "in_NORMALIZED00000000000002",
|
"id": "in_NORMALIZED00000000000002",
|
||||||
@@ -56,8 +45,6 @@
|
|||||||
"proration": false,
|
"proration": false,
|
||||||
"quantity": 6,
|
"quantity": 6,
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"tax_amounts": [],
|
|
||||||
"tax_rates": [],
|
|
||||||
"type": "invoiceitem"
|
"type": "invoiceitem"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -78,8 +65,6 @@
|
|||||||
"proration": false,
|
"proration": false,
|
||||||
"quantity": 1,
|
"quantity": 1,
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"tax_amounts": [],
|
|
||||||
"tax_rates": [],
|
|
||||||
"type": "invoiceitem"
|
"type": "invoiceitem"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -94,61 +79,40 @@
|
|||||||
"number": "NORMALI-0002",
|
"number": "NORMALI-0002",
|
||||||
"object": "invoice",
|
"object": "invoice",
|
||||||
"paid": true,
|
"paid": true,
|
||||||
"payment_intent": null,
|
|
||||||
"period_end": 1000000000,
|
"period_end": 1000000000,
|
||||||
"period_start": 1000000000,
|
"period_start": 1000000000,
|
||||||
"post_payment_credit_notes_amount": 0,
|
|
||||||
"pre_payment_credit_notes_amount": 0,
|
|
||||||
"receipt_number": null,
|
"receipt_number": null,
|
||||||
"starting_balance": 0,
|
"starting_balance": 0,
|
||||||
"statement_descriptor": "Zulip Standard",
|
"statement_descriptor": "Zulip Standard",
|
||||||
"status": "paid",
|
"status": "paid",
|
||||||
"status_transitions": {
|
|
||||||
"finalized_at": 1000000000,
|
|
||||||
"marked_uncollectible_at": null,
|
|
||||||
"paid_at": 1000000000,
|
|
||||||
"voided_at": null
|
|
||||||
},
|
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"subtotal": 0,
|
"subtotal": 0,
|
||||||
"tax": null,
|
"tax": 0,
|
||||||
"tax_percent": null,
|
"tax_percent": null,
|
||||||
"total": 0,
|
"total": 0,
|
||||||
"total_tax_amounts": [],
|
|
||||||
"webhooks_delivered_at": 1000000000
|
"webhooks_delivered_at": 1000000000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"account_country": "US",
|
|
||||||
"account_name": "Dev account",
|
|
||||||
"amount_due": 0,
|
"amount_due": 0,
|
||||||
"amount_paid": 0,
|
"amount_paid": 0,
|
||||||
"amount_remaining": 0,
|
"amount_remaining": 0,
|
||||||
"application_fee_amount": null,
|
"application_fee": null,
|
||||||
"attempt_count": 0,
|
"attempt_count": 0,
|
||||||
"attempted": true,
|
"attempted": true,
|
||||||
"auto_advance": false,
|
"auto_advance": false,
|
||||||
"billing": "charge_automatically",
|
"billing": "charge_automatically",
|
||||||
"billing_reason": "manual",
|
"billing_reason": "manual",
|
||||||
"charge": null,
|
"charge": null,
|
||||||
"collection_method": "charge_automatically",
|
|
||||||
"created": 1000000000,
|
|
||||||
"currency": "usd",
|
"currency": "usd",
|
||||||
"custom_fields": null,
|
"custom_fields": null,
|
||||||
"customer": "cus_NORMALIZED0001",
|
"customer": "cus_NORMALIZED0001",
|
||||||
"customer_address": null,
|
"date": 1000000000,
|
||||||
"customer_email": "hamlet@zulip.com",
|
|
||||||
"customer_name": null,
|
|
||||||
"customer_phone": null,
|
|
||||||
"customer_shipping": null,
|
|
||||||
"customer_tax_exempt": "none",
|
|
||||||
"customer_tax_ids": [],
|
|
||||||
"default_payment_method": null,
|
|
||||||
"default_source": null,
|
"default_source": null,
|
||||||
"default_tax_rates": [],
|
|
||||||
"description": "",
|
"description": "",
|
||||||
"discount": null,
|
"discount": null,
|
||||||
"due_date": 1000000000,
|
"due_date": 1000000000,
|
||||||
"ending_balance": 0,
|
"ending_balance": 0,
|
||||||
|
"finalized_at": 1000000000,
|
||||||
"footer": null,
|
"footer": null,
|
||||||
"hosted_invoice_url": "https://pay.stripe.com/invoice/invst_NORMALIZED0000000000000001",
|
"hosted_invoice_url": "https://pay.stripe.com/invoice/invst_NORMALIZED0000000000000001",
|
||||||
"id": "in_NORMALIZED00000000000001",
|
"id": "in_NORMALIZED00000000000001",
|
||||||
@@ -173,8 +137,6 @@
|
|||||||
"proration": false,
|
"proration": false,
|
||||||
"quantity": 6,
|
"quantity": 6,
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"tax_amounts": [],
|
|
||||||
"tax_rates": [],
|
|
||||||
"type": "invoiceitem"
|
"type": "invoiceitem"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -195,8 +157,6 @@
|
|||||||
"proration": false,
|
"proration": false,
|
||||||
"quantity": 1,
|
"quantity": 1,
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"tax_amounts": [],
|
|
||||||
"tax_rates": [],
|
|
||||||
"type": "invoiceitem"
|
"type": "invoiceitem"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -211,27 +171,17 @@
|
|||||||
"number": "NORMALI-0001",
|
"number": "NORMALI-0001",
|
||||||
"object": "invoice",
|
"object": "invoice",
|
||||||
"paid": true,
|
"paid": true,
|
||||||
"payment_intent": null,
|
|
||||||
"period_end": 1000000000,
|
"period_end": 1000000000,
|
||||||
"period_start": 1000000000,
|
"period_start": 1000000000,
|
||||||
"post_payment_credit_notes_amount": 0,
|
|
||||||
"pre_payment_credit_notes_amount": 0,
|
|
||||||
"receipt_number": null,
|
"receipt_number": null,
|
||||||
"starting_balance": 0,
|
"starting_balance": 0,
|
||||||
"statement_descriptor": "Zulip Standard",
|
"statement_descriptor": "Zulip Standard",
|
||||||
"status": "paid",
|
"status": "paid",
|
||||||
"status_transitions": {
|
|
||||||
"finalized_at": 1000000000,
|
|
||||||
"marked_uncollectible_at": null,
|
|
||||||
"paid_at": 1000000000,
|
|
||||||
"voided_at": null
|
|
||||||
},
|
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"subtotal": 0,
|
"subtotal": 0,
|
||||||
"tax": null,
|
"tax": 0,
|
||||||
"tax_percent": null,
|
"tax_percent": null,
|
||||||
"total": 0,
|
"total": 0,
|
||||||
"total_tax_amounts": [],
|
|
||||||
"webhooks_delivered_at": 1000000000
|
"webhooks_delivered_at": 1000000000
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -18,6 +18,5 @@
|
|||||||
"proration": false,
|
"proration": false,
|
||||||
"quantity": 1,
|
"quantity": 1,
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"tax_rates": [],
|
|
||||||
"unit_amount": -7200
|
"unit_amount": -7200
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,5 @@
|
|||||||
"proration": false,
|
"proration": false,
|
||||||
"quantity": 6,
|
"quantity": 6,
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"tax_rates": [],
|
|
||||||
"unit_amount": 1200
|
"unit_amount": 1200
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,5 @@
|
|||||||
"proration": false,
|
"proration": false,
|
||||||
"quantity": 1,
|
"quantity": 1,
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"tax_rates": [],
|
|
||||||
"unit_amount": -36000
|
"unit_amount": -36000
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,5 @@
|
|||||||
"proration": false,
|
"proration": false,
|
||||||
"quantity": 6,
|
"quantity": 6,
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"tax_rates": [],
|
|
||||||
"unit_amount": 6000
|
"unit_amount": 6000
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,19 +5,6 @@
|
|||||||
"application_fee": null,
|
"application_fee": null,
|
||||||
"application_fee_amount": null,
|
"application_fee_amount": null,
|
||||||
"balance_transaction": "txn_NORMALIZED00000000000001",
|
"balance_transaction": "txn_NORMALIZED00000000000001",
|
||||||
"billing_details": {
|
|
||||||
"address": {
|
|
||||||
"city": "Pacific",
|
|
||||||
"country": "United States",
|
|
||||||
"line1": "Under the sea,",
|
|
||||||
"line2": null,
|
|
||||||
"postal_code": "33333",
|
|
||||||
"state": null
|
|
||||||
},
|
|
||||||
"email": null,
|
|
||||||
"name": "Ada Starr",
|
|
||||||
"phone": null
|
|
||||||
},
|
|
||||||
"captured": true,
|
"captured": true,
|
||||||
"created": 1000000000,
|
"created": 1000000000,
|
||||||
"currency": "usd",
|
"currency": "usd",
|
||||||
@@ -45,26 +32,6 @@
|
|||||||
},
|
},
|
||||||
"paid": true,
|
"paid": true,
|
||||||
"payment_intent": null,
|
"payment_intent": null,
|
||||||
"payment_method": "card_NORMALIZED00000000000001",
|
|
||||||
"payment_method_details": {
|
|
||||||
"card": {
|
|
||||||
"brand": "visa",
|
|
||||||
"checks": {
|
|
||||||
"address_line1_check": "pass",
|
|
||||||
"address_postal_code_check": "pass",
|
|
||||||
"cvc_check": "pass"
|
|
||||||
},
|
|
||||||
"country": "US",
|
|
||||||
"exp_month": 3,
|
|
||||||
"exp_year": 2033,
|
|
||||||
"fingerprint": "NORMALIZED000001",
|
|
||||||
"funding": "credit",
|
|
||||||
"last4": "4242",
|
|
||||||
"three_d_secure": null,
|
|
||||||
"wallet": null
|
|
||||||
},
|
|
||||||
"type": "card"
|
|
||||||
},
|
|
||||||
"receipt_email": "hamlet@zulip.com",
|
"receipt_email": "hamlet@zulip.com",
|
||||||
"receipt_number": null,
|
"receipt_number": null,
|
||||||
"receipt_url": "https://pay.stripe.com/receipts/acct_NORMALIZED000001/ch_NORMALIZED00000000000001/rcpt_NORMALIZED000000000000000000001",
|
"receipt_url": "https://pay.stripe.com/receipts/acct_NORMALIZED000001/ch_NORMALIZED00000000000001/rcpt_NORMALIZED000000000000000000001",
|
||||||
@@ -105,7 +72,6 @@
|
|||||||
},
|
},
|
||||||
"source_transfer": null,
|
"source_transfer": null,
|
||||||
"statement_descriptor": "Zulip Standard",
|
"statement_descriptor": "Zulip Standard",
|
||||||
"statement_descriptor_suffix": "Zulip Standard",
|
|
||||||
"status": "succeeded",
|
"status": "succeeded",
|
||||||
"transfer_data": null,
|
"transfer_data": null,
|
||||||
"transfer_group": null
|
"transfer_group": null
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
{
|
{
|
||||||
"account_balance": 0,
|
"account_balance": 0,
|
||||||
"address": null,
|
|
||||||
"balance": 0,
|
|
||||||
"created": 1000000000,
|
"created": 1000000000,
|
||||||
"currency": null,
|
"currency": null,
|
||||||
"default_source": "card_NORMALIZED00000000000001",
|
"default_source": "card_NORMALIZED00000000000001",
|
||||||
@@ -13,7 +11,6 @@
|
|||||||
"invoice_prefix": "NORMA01",
|
"invoice_prefix": "NORMA01",
|
||||||
"invoice_settings": {
|
"invoice_settings": {
|
||||||
"custom_fields": null,
|
"custom_fields": null,
|
||||||
"default_payment_method": null,
|
|
||||||
"footer": null
|
"footer": null
|
||||||
},
|
},
|
||||||
"livemode": false,
|
"livemode": false,
|
||||||
@@ -21,10 +18,7 @@
|
|||||||
"realm_id": "1",
|
"realm_id": "1",
|
||||||
"realm_str": "zulip"
|
"realm_str": "zulip"
|
||||||
},
|
},
|
||||||
"name": null,
|
|
||||||
"object": "customer",
|
"object": "customer",
|
||||||
"phone": null,
|
|
||||||
"preferred_locales": [],
|
|
||||||
"shipping": null,
|
"shipping": null,
|
||||||
"sources": {
|
"sources": {
|
||||||
"data": [
|
"data": [
|
||||||
@@ -66,14 +60,6 @@
|
|||||||
"total_count": 0,
|
"total_count": 0,
|
||||||
"url": "/v1/customers/cus_NORMALIZED0001/subscriptions"
|
"url": "/v1/customers/cus_NORMALIZED0001/subscriptions"
|
||||||
},
|
},
|
||||||
"tax_exempt": "none",
|
|
||||||
"tax_ids": {
|
|
||||||
"data": [],
|
|
||||||
"has_more": false,
|
|
||||||
"object": "list",
|
|
||||||
"total_count": 0,
|
|
||||||
"url": "/v1/customers/cus_NORMALIZED0001/tax_ids"
|
|
||||||
},
|
|
||||||
"tax_info": null,
|
"tax_info": null,
|
||||||
"tax_info_verification": null
|
"tax_info_verification": null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
{
|
{
|
||||||
"account_balance": 0,
|
"account_balance": 0,
|
||||||
"address": null,
|
|
||||||
"balance": 0,
|
|
||||||
"created": 1000000000,
|
"created": 1000000000,
|
||||||
"currency": "usd",
|
"currency": "usd",
|
||||||
"default_source": {
|
"default_source": {
|
||||||
@@ -37,7 +35,6 @@
|
|||||||
"invoice_prefix": "NORMA01",
|
"invoice_prefix": "NORMA01",
|
||||||
"invoice_settings": {
|
"invoice_settings": {
|
||||||
"custom_fields": null,
|
"custom_fields": null,
|
||||||
"default_payment_method": null,
|
|
||||||
"footer": null
|
"footer": null
|
||||||
},
|
},
|
||||||
"livemode": false,
|
"livemode": false,
|
||||||
@@ -45,10 +42,7 @@
|
|||||||
"realm_id": "1",
|
"realm_id": "1",
|
||||||
"realm_str": "zulip"
|
"realm_str": "zulip"
|
||||||
},
|
},
|
||||||
"name": null,
|
|
||||||
"object": "customer",
|
"object": "customer",
|
||||||
"phone": null,
|
|
||||||
"preferred_locales": [],
|
|
||||||
"shipping": null,
|
"shipping": null,
|
||||||
"sources": {
|
"sources": {
|
||||||
"data": [
|
"data": [
|
||||||
@@ -90,14 +84,6 @@
|
|||||||
"total_count": 0,
|
"total_count": 0,
|
||||||
"url": "/v1/customers/cus_NORMALIZED0001/subscriptions"
|
"url": "/v1/customers/cus_NORMALIZED0001/subscriptions"
|
||||||
},
|
},
|
||||||
"tax_exempt": "none",
|
|
||||||
"tax_ids": {
|
|
||||||
"data": [],
|
|
||||||
"has_more": false,
|
|
||||||
"object": "list",
|
|
||||||
"total_count": 0,
|
|
||||||
"url": "/v1/customers/cus_NORMALIZED0001/tax_ids"
|
|
||||||
},
|
|
||||||
"tax_info": null,
|
"tax_info": null,
|
||||||
"tax_info_verification": null
|
"tax_info_verification": null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
{
|
{
|
||||||
"account_balance": 0,
|
"account_balance": 0,
|
||||||
"address": null,
|
|
||||||
"balance": 0,
|
|
||||||
"created": 1000000000,
|
"created": 1000000000,
|
||||||
"currency": "usd",
|
"currency": "usd",
|
||||||
"default_source": {
|
"default_source": {
|
||||||
@@ -37,7 +35,6 @@
|
|||||||
"invoice_prefix": "NORMA01",
|
"invoice_prefix": "NORMA01",
|
||||||
"invoice_settings": {
|
"invoice_settings": {
|
||||||
"custom_fields": null,
|
"custom_fields": null,
|
||||||
"default_payment_method": null,
|
|
||||||
"footer": null
|
"footer": null
|
||||||
},
|
},
|
||||||
"livemode": false,
|
"livemode": false,
|
||||||
@@ -45,10 +42,7 @@
|
|||||||
"realm_id": "1",
|
"realm_id": "1",
|
||||||
"realm_str": "zulip"
|
"realm_str": "zulip"
|
||||||
},
|
},
|
||||||
"name": null,
|
|
||||||
"object": "customer",
|
"object": "customer",
|
||||||
"phone": null,
|
|
||||||
"preferred_locales": [],
|
|
||||||
"shipping": null,
|
"shipping": null,
|
||||||
"sources": {
|
"sources": {
|
||||||
"data": [
|
"data": [
|
||||||
@@ -90,14 +84,6 @@
|
|||||||
"total_count": 0,
|
"total_count": 0,
|
||||||
"url": "/v1/customers/cus_NORMALIZED0001/subscriptions"
|
"url": "/v1/customers/cus_NORMALIZED0001/subscriptions"
|
||||||
},
|
},
|
||||||
"tax_exempt": "none",
|
|
||||||
"tax_ids": {
|
|
||||||
"data": [],
|
|
||||||
"has_more": false,
|
|
||||||
"object": "list",
|
|
||||||
"total_count": 0,
|
|
||||||
"url": "/v1/customers/cus_NORMALIZED0001/tax_ids"
|
|
||||||
},
|
|
||||||
"tax_info": null,
|
"tax_info": null,
|
||||||
"tax_info_verification": null
|
"tax_info_verification": null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,35 +1,24 @@
|
|||||||
{
|
{
|
||||||
"account_country": "US",
|
|
||||||
"account_name": "Dev account",
|
|
||||||
"amount_due": 0,
|
"amount_due": 0,
|
||||||
"amount_paid": 0,
|
"amount_paid": 0,
|
||||||
"amount_remaining": 0,
|
"amount_remaining": 0,
|
||||||
"application_fee_amount": null,
|
"application_fee": null,
|
||||||
"attempt_count": 0,
|
"attempt_count": 0,
|
||||||
"attempted": false,
|
"attempted": false,
|
||||||
"auto_advance": true,
|
"auto_advance": true,
|
||||||
"billing": "charge_automatically",
|
"billing": "charge_automatically",
|
||||||
"billing_reason": "manual",
|
"billing_reason": "manual",
|
||||||
"charge": null,
|
"charge": null,
|
||||||
"collection_method": "charge_automatically",
|
|
||||||
"created": 1000000000,
|
|
||||||
"currency": "usd",
|
"currency": "usd",
|
||||||
"custom_fields": null,
|
"custom_fields": null,
|
||||||
"customer": "cus_NORMALIZED0001",
|
"customer": "cus_NORMALIZED0001",
|
||||||
"customer_address": null,
|
"date": 1000000000,
|
||||||
"customer_email": "hamlet@zulip.com",
|
|
||||||
"customer_name": null,
|
|
||||||
"customer_phone": null,
|
|
||||||
"customer_shipping": null,
|
|
||||||
"customer_tax_exempt": "none",
|
|
||||||
"customer_tax_ids": [],
|
|
||||||
"default_payment_method": null,
|
|
||||||
"default_source": null,
|
"default_source": null,
|
||||||
"default_tax_rates": [],
|
|
||||||
"description": "",
|
"description": "",
|
||||||
"discount": null,
|
"discount": null,
|
||||||
"due_date": null,
|
"due_date": null,
|
||||||
"ending_balance": null,
|
"ending_balance": null,
|
||||||
|
"finalized_at": null,
|
||||||
"footer": null,
|
"footer": null,
|
||||||
"hosted_invoice_url": null,
|
"hosted_invoice_url": null,
|
||||||
"id": "in_NORMALIZED00000000000001",
|
"id": "in_NORMALIZED00000000000001",
|
||||||
@@ -54,8 +43,6 @@
|
|||||||
"proration": false,
|
"proration": false,
|
||||||
"quantity": 6,
|
"quantity": 6,
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"tax_amounts": [],
|
|
||||||
"tax_rates": [],
|
|
||||||
"type": "invoiceitem"
|
"type": "invoiceitem"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -76,8 +63,6 @@
|
|||||||
"proration": false,
|
"proration": false,
|
||||||
"quantity": 1,
|
"quantity": 1,
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"tax_amounts": [],
|
|
||||||
"tax_rates": [],
|
|
||||||
"type": "invoiceitem"
|
"type": "invoiceitem"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -92,26 +77,16 @@
|
|||||||
"number": "NORMALI-0001",
|
"number": "NORMALI-0001",
|
||||||
"object": "invoice",
|
"object": "invoice",
|
||||||
"paid": false,
|
"paid": false,
|
||||||
"payment_intent": null,
|
|
||||||
"period_end": 1000000000,
|
"period_end": 1000000000,
|
||||||
"period_start": 1000000000,
|
"period_start": 1000000000,
|
||||||
"post_payment_credit_notes_amount": 0,
|
|
||||||
"pre_payment_credit_notes_amount": 0,
|
|
||||||
"receipt_number": null,
|
"receipt_number": null,
|
||||||
"starting_balance": 0,
|
"starting_balance": 0,
|
||||||
"statement_descriptor": "Zulip Standard",
|
"statement_descriptor": "Zulip Standard",
|
||||||
"status": "draft",
|
"status": "draft",
|
||||||
"status_transitions": {
|
|
||||||
"finalized_at": null,
|
|
||||||
"marked_uncollectible_at": null,
|
|
||||||
"paid_at": null,
|
|
||||||
"voided_at": null
|
|
||||||
},
|
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"subtotal": 0,
|
"subtotal": 0,
|
||||||
"tax": null,
|
"tax": 0,
|
||||||
"tax_percent": null,
|
"tax_percent": null,
|
||||||
"total": 0,
|
"total": 0,
|
||||||
"total_tax_amounts": [],
|
|
||||||
"webhooks_delivered_at": null
|
"webhooks_delivered_at": null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,35 +1,24 @@
|
|||||||
{
|
{
|
||||||
"account_country": "US",
|
|
||||||
"account_name": "Dev account",
|
|
||||||
"amount_due": 0,
|
"amount_due": 0,
|
||||||
"amount_paid": 0,
|
"amount_paid": 0,
|
||||||
"amount_remaining": 0,
|
"amount_remaining": 0,
|
||||||
"application_fee_amount": null,
|
"application_fee": null,
|
||||||
"attempt_count": 0,
|
"attempt_count": 0,
|
||||||
"attempted": true,
|
"attempted": true,
|
||||||
"auto_advance": false,
|
"auto_advance": false,
|
||||||
"billing": "charge_automatically",
|
"billing": "charge_automatically",
|
||||||
"billing_reason": "manual",
|
"billing_reason": "manual",
|
||||||
"charge": null,
|
"charge": null,
|
||||||
"collection_method": "charge_automatically",
|
|
||||||
"created": 1000000000,
|
|
||||||
"currency": "usd",
|
"currency": "usd",
|
||||||
"custom_fields": null,
|
"custom_fields": null,
|
||||||
"customer": "cus_NORMALIZED0001",
|
"customer": "cus_NORMALIZED0001",
|
||||||
"customer_address": null,
|
"date": 1000000000,
|
||||||
"customer_email": "hamlet@zulip.com",
|
|
||||||
"customer_name": null,
|
|
||||||
"customer_phone": null,
|
|
||||||
"customer_shipping": null,
|
|
||||||
"customer_tax_exempt": "none",
|
|
||||||
"customer_tax_ids": [],
|
|
||||||
"default_payment_method": null,
|
|
||||||
"default_source": null,
|
"default_source": null,
|
||||||
"default_tax_rates": [],
|
|
||||||
"description": "",
|
"description": "",
|
||||||
"discount": null,
|
"discount": null,
|
||||||
"due_date": 1000000000,
|
"due_date": 1000000000,
|
||||||
"ending_balance": 0,
|
"ending_balance": 0,
|
||||||
|
"finalized_at": 1000000000,
|
||||||
"footer": null,
|
"footer": null,
|
||||||
"hosted_invoice_url": "https://pay.stripe.com/invoice/invst_NORMALIZED0000000000000001",
|
"hosted_invoice_url": "https://pay.stripe.com/invoice/invst_NORMALIZED0000000000000001",
|
||||||
"id": "in_NORMALIZED00000000000001",
|
"id": "in_NORMALIZED00000000000001",
|
||||||
@@ -54,8 +43,6 @@
|
|||||||
"proration": false,
|
"proration": false,
|
||||||
"quantity": 6,
|
"quantity": 6,
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"tax_amounts": [],
|
|
||||||
"tax_rates": [],
|
|
||||||
"type": "invoiceitem"
|
"type": "invoiceitem"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -76,8 +63,6 @@
|
|||||||
"proration": false,
|
"proration": false,
|
||||||
"quantity": 1,
|
"quantity": 1,
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"tax_amounts": [],
|
|
||||||
"tax_rates": [],
|
|
||||||
"type": "invoiceitem"
|
"type": "invoiceitem"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -92,26 +77,16 @@
|
|||||||
"number": "NORMALI-0001",
|
"number": "NORMALI-0001",
|
||||||
"object": "invoice",
|
"object": "invoice",
|
||||||
"paid": true,
|
"paid": true,
|
||||||
"payment_intent": null,
|
|
||||||
"period_end": 1000000000,
|
"period_end": 1000000000,
|
||||||
"period_start": 1000000000,
|
"period_start": 1000000000,
|
||||||
"post_payment_credit_notes_amount": 0,
|
|
||||||
"pre_payment_credit_notes_amount": 0,
|
|
||||||
"receipt_number": null,
|
"receipt_number": null,
|
||||||
"starting_balance": 0,
|
"starting_balance": 0,
|
||||||
"statement_descriptor": "Zulip Standard",
|
"statement_descriptor": "Zulip Standard",
|
||||||
"status": "paid",
|
"status": "paid",
|
||||||
"status_transitions": {
|
|
||||||
"finalized_at": 1000000000,
|
|
||||||
"marked_uncollectible_at": null,
|
|
||||||
"paid_at": 1000000000,
|
|
||||||
"voided_at": null
|
|
||||||
},
|
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"subtotal": 0,
|
"subtotal": 0,
|
||||||
"tax": null,
|
"tax": 0,
|
||||||
"tax_percent": null,
|
"tax_percent": null,
|
||||||
"total": 0,
|
"total": 0,
|
||||||
"total_tax_amounts": [],
|
|
||||||
"webhooks_delivered_at": 1000000000
|
"webhooks_delivered_at": 1000000000
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,5 @@
|
|||||||
"proration": false,
|
"proration": false,
|
||||||
"quantity": 1,
|
"quantity": 1,
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"tax_rates": [],
|
|
||||||
"unit_amount": -48000
|
"unit_amount": -48000
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,5 @@
|
|||||||
"proration": false,
|
"proration": false,
|
||||||
"quantity": 6,
|
"quantity": 6,
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"tax_rates": [],
|
|
||||||
"unit_amount": 8000
|
"unit_amount": 8000
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
{
|
{
|
||||||
"account_balance": 0,
|
"account_balance": 0,
|
||||||
"address": null,
|
|
||||||
"balance": 0,
|
|
||||||
"created": 1000000000,
|
"created": 1000000000,
|
||||||
"currency": null,
|
"currency": null,
|
||||||
"default_source": null,
|
"default_source": null,
|
||||||
@@ -13,7 +11,6 @@
|
|||||||
"invoice_prefix": "NORMA01",
|
"invoice_prefix": "NORMA01",
|
||||||
"invoice_settings": {
|
"invoice_settings": {
|
||||||
"custom_fields": null,
|
"custom_fields": null,
|
||||||
"default_payment_method": null,
|
|
||||||
"footer": null
|
"footer": null
|
||||||
},
|
},
|
||||||
"livemode": false,
|
"livemode": false,
|
||||||
@@ -21,10 +18,7 @@
|
|||||||
"realm_id": "1",
|
"realm_id": "1",
|
||||||
"realm_str": "zulip"
|
"realm_str": "zulip"
|
||||||
},
|
},
|
||||||
"name": null,
|
|
||||||
"object": "customer",
|
"object": "customer",
|
||||||
"phone": null,
|
|
||||||
"preferred_locales": [],
|
|
||||||
"shipping": null,
|
"shipping": null,
|
||||||
"sources": {
|
"sources": {
|
||||||
"data": [],
|
"data": [],
|
||||||
@@ -40,14 +34,6 @@
|
|||||||
"total_count": 0,
|
"total_count": 0,
|
||||||
"url": "/v1/customers/cus_NORMALIZED0001/subscriptions"
|
"url": "/v1/customers/cus_NORMALIZED0001/subscriptions"
|
||||||
},
|
},
|
||||||
"tax_exempt": "none",
|
|
||||||
"tax_ids": {
|
|
||||||
"data": [],
|
|
||||||
"has_more": false,
|
|
||||||
"object": "list",
|
|
||||||
"total_count": 0,
|
|
||||||
"url": "/v1/customers/cus_NORMALIZED0001/tax_ids"
|
|
||||||
},
|
|
||||||
"tax_info": null,
|
"tax_info": null,
|
||||||
"tax_info_verification": null
|
"tax_info_verification": null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,35 +1,24 @@
|
|||||||
{
|
{
|
||||||
"account_country": "US",
|
|
||||||
"account_name": "Dev account",
|
|
||||||
"amount_due": 984000,
|
"amount_due": 984000,
|
||||||
"amount_paid": 0,
|
"amount_paid": 0,
|
||||||
"amount_remaining": 984000,
|
"amount_remaining": 984000,
|
||||||
"application_fee_amount": null,
|
"application_fee": null,
|
||||||
"attempt_count": 0,
|
"attempt_count": 0,
|
||||||
"attempted": false,
|
"attempted": false,
|
||||||
"auto_advance": true,
|
"auto_advance": true,
|
||||||
"billing": "send_invoice",
|
"billing": "send_invoice",
|
||||||
"billing_reason": "manual",
|
"billing_reason": "manual",
|
||||||
"charge": null,
|
"charge": null,
|
||||||
"collection_method": "send_invoice",
|
|
||||||
"created": 1000000000,
|
|
||||||
"currency": "usd",
|
"currency": "usd",
|
||||||
"custom_fields": null,
|
"custom_fields": null,
|
||||||
"customer": "cus_NORMALIZED0001",
|
"customer": "cus_NORMALIZED0001",
|
||||||
"customer_address": null,
|
"date": 1000000000,
|
||||||
"customer_email": "hamlet@zulip.com",
|
|
||||||
"customer_name": null,
|
|
||||||
"customer_phone": null,
|
|
||||||
"customer_shipping": null,
|
|
||||||
"customer_tax_exempt": "none",
|
|
||||||
"customer_tax_ids": [],
|
|
||||||
"default_payment_method": null,
|
|
||||||
"default_source": null,
|
"default_source": null,
|
||||||
"default_tax_rates": [],
|
|
||||||
"description": "",
|
"description": "",
|
||||||
"discount": null,
|
"discount": null,
|
||||||
"due_date": 1000000000,
|
"due_date": 1000000000,
|
||||||
"ending_balance": null,
|
"ending_balance": null,
|
||||||
|
"finalized_at": null,
|
||||||
"footer": null,
|
"footer": null,
|
||||||
"hosted_invoice_url": null,
|
"hosted_invoice_url": null,
|
||||||
"id": "in_NORMALIZED00000000000001",
|
"id": "in_NORMALIZED00000000000001",
|
||||||
@@ -54,8 +43,6 @@
|
|||||||
"proration": false,
|
"proration": false,
|
||||||
"quantity": 123,
|
"quantity": 123,
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"tax_amounts": [],
|
|
||||||
"tax_rates": [],
|
|
||||||
"type": "invoiceitem"
|
"type": "invoiceitem"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -70,26 +57,16 @@
|
|||||||
"number": "NORMALI-0001",
|
"number": "NORMALI-0001",
|
||||||
"object": "invoice",
|
"object": "invoice",
|
||||||
"paid": false,
|
"paid": false,
|
||||||
"payment_intent": null,
|
|
||||||
"period_end": 1000000000,
|
"period_end": 1000000000,
|
||||||
"period_start": 1000000000,
|
"period_start": 1000000000,
|
||||||
"post_payment_credit_notes_amount": 0,
|
|
||||||
"pre_payment_credit_notes_amount": 0,
|
|
||||||
"receipt_number": null,
|
"receipt_number": null,
|
||||||
"starting_balance": 0,
|
"starting_balance": 0,
|
||||||
"statement_descriptor": "Zulip Standard",
|
"statement_descriptor": "Zulip Standard",
|
||||||
"status": "draft",
|
"status": "draft",
|
||||||
"status_transitions": {
|
|
||||||
"finalized_at": null,
|
|
||||||
"marked_uncollectible_at": null,
|
|
||||||
"paid_at": null,
|
|
||||||
"voided_at": null
|
|
||||||
},
|
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"subtotal": 984000,
|
"subtotal": 984000,
|
||||||
"tax": null,
|
"tax": 0,
|
||||||
"tax_percent": null,
|
"tax_percent": null,
|
||||||
"total": 984000,
|
"total": 984000,
|
||||||
"total_tax_amounts": [],
|
|
||||||
"webhooks_delivered_at": null
|
"webhooks_delivered_at": null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,35 +1,24 @@
|
|||||||
{
|
{
|
||||||
"account_country": "US",
|
|
||||||
"account_name": "Dev account",
|
|
||||||
"amount_due": 100,
|
"amount_due": 100,
|
||||||
"amount_paid": 0,
|
"amount_paid": 0,
|
||||||
"amount_remaining": 100,
|
"amount_remaining": 100,
|
||||||
"application_fee_amount": null,
|
"application_fee": null,
|
||||||
"attempt_count": 0,
|
"attempt_count": 0,
|
||||||
"attempted": false,
|
"attempted": false,
|
||||||
"auto_advance": true,
|
"auto_advance": true,
|
||||||
"billing": "send_invoice",
|
"billing": "send_invoice",
|
||||||
"billing_reason": "manual",
|
"billing_reason": "manual",
|
||||||
"charge": null,
|
"charge": null,
|
||||||
"collection_method": "send_invoice",
|
|
||||||
"created": 1000000000,
|
|
||||||
"currency": "usd",
|
"currency": "usd",
|
||||||
"custom_fields": null,
|
"custom_fields": null,
|
||||||
"customer": "cus_NORMALIZED0001",
|
"customer": "cus_NORMALIZED0001",
|
||||||
"customer_address": null,
|
"date": 1000000000,
|
||||||
"customer_email": "hamlet@zulip.com",
|
|
||||||
"customer_name": null,
|
|
||||||
"customer_phone": null,
|
|
||||||
"customer_shipping": null,
|
|
||||||
"customer_tax_exempt": "none",
|
|
||||||
"customer_tax_ids": [],
|
|
||||||
"default_payment_method": null,
|
|
||||||
"default_source": null,
|
"default_source": null,
|
||||||
"default_tax_rates": [],
|
|
||||||
"description": "",
|
"description": "",
|
||||||
"discount": null,
|
"discount": null,
|
||||||
"due_date": 1000000000,
|
"due_date": 1000000000,
|
||||||
"ending_balance": null,
|
"ending_balance": null,
|
||||||
|
"finalized_at": null,
|
||||||
"footer": null,
|
"footer": null,
|
||||||
"hosted_invoice_url": null,
|
"hosted_invoice_url": null,
|
||||||
"id": "in_NORMALIZED00000000000002",
|
"id": "in_NORMALIZED00000000000002",
|
||||||
@@ -54,8 +43,6 @@
|
|||||||
"proration": false,
|
"proration": false,
|
||||||
"quantity": 1,
|
"quantity": 1,
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"tax_amounts": [],
|
|
||||||
"tax_rates": [],
|
|
||||||
"type": "invoiceitem"
|
"type": "invoiceitem"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -70,26 +57,16 @@
|
|||||||
"number": "NORMALI-0002",
|
"number": "NORMALI-0002",
|
||||||
"object": "invoice",
|
"object": "invoice",
|
||||||
"paid": false,
|
"paid": false,
|
||||||
"payment_intent": null,
|
|
||||||
"period_end": 1000000000,
|
"period_end": 1000000000,
|
||||||
"period_start": 1000000000,
|
"period_start": 1000000000,
|
||||||
"post_payment_credit_notes_amount": 0,
|
|
||||||
"pre_payment_credit_notes_amount": 0,
|
|
||||||
"receipt_number": null,
|
"receipt_number": null,
|
||||||
"starting_balance": 0,
|
"starting_balance": 0,
|
||||||
"statement_descriptor": "Zulip Standard",
|
"statement_descriptor": "Zulip Standard",
|
||||||
"status": "draft",
|
"status": "draft",
|
||||||
"status_transitions": {
|
|
||||||
"finalized_at": null,
|
|
||||||
"marked_uncollectible_at": null,
|
|
||||||
"paid_at": null,
|
|
||||||
"voided_at": null
|
|
||||||
},
|
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"subtotal": 100,
|
"subtotal": 100,
|
||||||
"tax": null,
|
"tax": 0,
|
||||||
"tax_percent": null,
|
"tax_percent": null,
|
||||||
"total": 100,
|
"total": 100,
|
||||||
"total_tax_amounts": [],
|
|
||||||
"webhooks_delivered_at": null
|
"webhooks_delivered_at": null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,35 +1,24 @@
|
|||||||
{
|
{
|
||||||
"account_country": "US",
|
|
||||||
"account_name": "Dev account",
|
|
||||||
"amount_due": 984000,
|
"amount_due": 984000,
|
||||||
"amount_paid": 0,
|
"amount_paid": 0,
|
||||||
"amount_remaining": 984000,
|
"amount_remaining": 984000,
|
||||||
"application_fee_amount": null,
|
"application_fee": null,
|
||||||
"attempt_count": 0,
|
"attempt_count": 0,
|
||||||
"attempted": false,
|
"attempted": false,
|
||||||
"auto_advance": true,
|
"auto_advance": true,
|
||||||
"billing": "send_invoice",
|
"billing": "send_invoice",
|
||||||
"billing_reason": "manual",
|
"billing_reason": "manual",
|
||||||
"charge": null,
|
"charge": null,
|
||||||
"collection_method": "send_invoice",
|
|
||||||
"created": 1000000000,
|
|
||||||
"currency": "usd",
|
"currency": "usd",
|
||||||
"custom_fields": null,
|
"custom_fields": null,
|
||||||
"customer": "cus_NORMALIZED0001",
|
"customer": "cus_NORMALIZED0001",
|
||||||
"customer_address": null,
|
"date": 1000000000,
|
||||||
"customer_email": "hamlet@zulip.com",
|
|
||||||
"customer_name": null,
|
|
||||||
"customer_phone": null,
|
|
||||||
"customer_shipping": null,
|
|
||||||
"customer_tax_exempt": "none",
|
|
||||||
"customer_tax_ids": [],
|
|
||||||
"default_payment_method": null,
|
|
||||||
"default_source": null,
|
"default_source": null,
|
||||||
"default_tax_rates": [],
|
|
||||||
"description": "",
|
"description": "",
|
||||||
"discount": null,
|
"discount": null,
|
||||||
"due_date": 1000000000,
|
"due_date": 1000000000,
|
||||||
"ending_balance": 0,
|
"ending_balance": 0,
|
||||||
|
"finalized_at": 1000000000,
|
||||||
"footer": null,
|
"footer": null,
|
||||||
"hosted_invoice_url": "https://pay.stripe.com/invoice/invst_NORMALIZED0000000000000001",
|
"hosted_invoice_url": "https://pay.stripe.com/invoice/invst_NORMALIZED0000000000000001",
|
||||||
"id": "in_NORMALIZED00000000000001",
|
"id": "in_NORMALIZED00000000000001",
|
||||||
@@ -54,8 +43,6 @@
|
|||||||
"proration": false,
|
"proration": false,
|
||||||
"quantity": 123,
|
"quantity": 123,
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"tax_amounts": [],
|
|
||||||
"tax_rates": [],
|
|
||||||
"type": "invoiceitem"
|
"type": "invoiceitem"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -70,26 +57,16 @@
|
|||||||
"number": "NORMALI-0001",
|
"number": "NORMALI-0001",
|
||||||
"object": "invoice",
|
"object": "invoice",
|
||||||
"paid": false,
|
"paid": false,
|
||||||
"payment_intent": "pi_1F96B0Gh0CmXqmnwFFVAGxWQ",
|
|
||||||
"period_end": 1000000000,
|
"period_end": 1000000000,
|
||||||
"period_start": 1000000000,
|
"period_start": 1000000000,
|
||||||
"post_payment_credit_notes_amount": 0,
|
|
||||||
"pre_payment_credit_notes_amount": 0,
|
|
||||||
"receipt_number": null,
|
"receipt_number": null,
|
||||||
"starting_balance": 0,
|
"starting_balance": 0,
|
||||||
"statement_descriptor": "Zulip Standard",
|
"statement_descriptor": "Zulip Standard",
|
||||||
"status": "open",
|
"status": "open",
|
||||||
"status_transitions": {
|
|
||||||
"finalized_at": 1000000000,
|
|
||||||
"marked_uncollectible_at": null,
|
|
||||||
"paid_at": null,
|
|
||||||
"voided_at": null
|
|
||||||
},
|
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"subtotal": 984000,
|
"subtotal": 984000,
|
||||||
"tax": null,
|
"tax": 0,
|
||||||
"tax_percent": null,
|
"tax_percent": null,
|
||||||
"total": 984000,
|
"total": 984000,
|
||||||
"total_tax_amounts": [],
|
|
||||||
"webhooks_delivered_at": 1000000000
|
"webhooks_delivered_at": 1000000000
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,35 +1,24 @@
|
|||||||
{
|
{
|
||||||
"account_country": "US",
|
|
||||||
"account_name": "Dev account",
|
|
||||||
"amount_due": 100,
|
"amount_due": 100,
|
||||||
"amount_paid": 0,
|
"amount_paid": 0,
|
||||||
"amount_remaining": 100,
|
"amount_remaining": 100,
|
||||||
"application_fee_amount": null,
|
"application_fee": null,
|
||||||
"attempt_count": 0,
|
"attempt_count": 0,
|
||||||
"attempted": false,
|
"attempted": false,
|
||||||
"auto_advance": true,
|
"auto_advance": true,
|
||||||
"billing": "send_invoice",
|
"billing": "send_invoice",
|
||||||
"billing_reason": "manual",
|
"billing_reason": "manual",
|
||||||
"charge": null,
|
"charge": null,
|
||||||
"collection_method": "send_invoice",
|
|
||||||
"created": 1000000000,
|
|
||||||
"currency": "usd",
|
"currency": "usd",
|
||||||
"custom_fields": null,
|
"custom_fields": null,
|
||||||
"customer": "cus_NORMALIZED0001",
|
"customer": "cus_NORMALIZED0001",
|
||||||
"customer_address": null,
|
"date": 1000000000,
|
||||||
"customer_email": "hamlet@zulip.com",
|
|
||||||
"customer_name": null,
|
|
||||||
"customer_phone": null,
|
|
||||||
"customer_shipping": null,
|
|
||||||
"customer_tax_exempt": "none",
|
|
||||||
"customer_tax_ids": [],
|
|
||||||
"default_payment_method": null,
|
|
||||||
"default_source": null,
|
"default_source": null,
|
||||||
"default_tax_rates": [],
|
|
||||||
"description": "",
|
"description": "",
|
||||||
"discount": null,
|
"discount": null,
|
||||||
"due_date": 1000000000,
|
"due_date": 1000000000,
|
||||||
"ending_balance": 0,
|
"ending_balance": 0,
|
||||||
|
"finalized_at": 1000000000,
|
||||||
"footer": null,
|
"footer": null,
|
||||||
"hosted_invoice_url": "https://pay.stripe.com/invoice/invst_NORMALIZED0000000000000002",
|
"hosted_invoice_url": "https://pay.stripe.com/invoice/invst_NORMALIZED0000000000000002",
|
||||||
"id": "in_NORMALIZED00000000000002",
|
"id": "in_NORMALIZED00000000000002",
|
||||||
@@ -54,8 +43,6 @@
|
|||||||
"proration": false,
|
"proration": false,
|
||||||
"quantity": 1,
|
"quantity": 1,
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"tax_amounts": [],
|
|
||||||
"tax_rates": [],
|
|
||||||
"type": "invoiceitem"
|
"type": "invoiceitem"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -70,26 +57,16 @@
|
|||||||
"number": "NORMALI-0002",
|
"number": "NORMALI-0002",
|
||||||
"object": "invoice",
|
"object": "invoice",
|
||||||
"paid": false,
|
"paid": false,
|
||||||
"payment_intent": "pi_1F96B2Gh0CmXqmnwZLKqLOFy",
|
|
||||||
"period_end": 1000000000,
|
"period_end": 1000000000,
|
||||||
"period_start": 1000000000,
|
"period_start": 1000000000,
|
||||||
"post_payment_credit_notes_amount": 0,
|
|
||||||
"pre_payment_credit_notes_amount": 0,
|
|
||||||
"receipt_number": null,
|
"receipt_number": null,
|
||||||
"starting_balance": 0,
|
"starting_balance": 0,
|
||||||
"statement_descriptor": "Zulip Standard",
|
"statement_descriptor": "Zulip Standard",
|
||||||
"status": "open",
|
"status": "open",
|
||||||
"status_transitions": {
|
|
||||||
"finalized_at": 1000000000,
|
|
||||||
"marked_uncollectible_at": null,
|
|
||||||
"paid_at": null,
|
|
||||||
"voided_at": null
|
|
||||||
},
|
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"subtotal": 100,
|
"subtotal": 100,
|
||||||
"tax": null,
|
"tax": 0,
|
||||||
"tax_percent": null,
|
"tax_percent": null,
|
||||||
"total": 100,
|
"total": 100,
|
||||||
"total_tax_amounts": [],
|
|
||||||
"webhooks_delivered_at": 1000000000
|
"webhooks_delivered_at": 1000000000
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,37 +1,26 @@
|
|||||||
{
|
{
|
||||||
"data": [
|
"data": [
|
||||||
{
|
{
|
||||||
"account_country": "US",
|
|
||||||
"account_name": "Dev account",
|
|
||||||
"amount_due": 100,
|
"amount_due": 100,
|
||||||
"amount_paid": 0,
|
"amount_paid": 0,
|
||||||
"amount_remaining": 100,
|
"amount_remaining": 100,
|
||||||
"application_fee_amount": null,
|
"application_fee": null,
|
||||||
"attempt_count": 0,
|
"attempt_count": 0,
|
||||||
"attempted": false,
|
"attempted": false,
|
||||||
"auto_advance": true,
|
"auto_advance": true,
|
||||||
"billing": "send_invoice",
|
"billing": "send_invoice",
|
||||||
"billing_reason": "manual",
|
"billing_reason": "manual",
|
||||||
"charge": null,
|
"charge": null,
|
||||||
"collection_method": "send_invoice",
|
|
||||||
"created": 1000000000,
|
|
||||||
"currency": "usd",
|
"currency": "usd",
|
||||||
"custom_fields": null,
|
"custom_fields": null,
|
||||||
"customer": "cus_NORMALIZED0001",
|
"customer": "cus_NORMALIZED0001",
|
||||||
"customer_address": null,
|
"date": 1000000000,
|
||||||
"customer_email": "hamlet@zulip.com",
|
|
||||||
"customer_name": null,
|
|
||||||
"customer_phone": null,
|
|
||||||
"customer_shipping": null,
|
|
||||||
"customer_tax_exempt": "none",
|
|
||||||
"customer_tax_ids": [],
|
|
||||||
"default_payment_method": null,
|
|
||||||
"default_source": null,
|
"default_source": null,
|
||||||
"default_tax_rates": [],
|
|
||||||
"description": "",
|
"description": "",
|
||||||
"discount": null,
|
"discount": null,
|
||||||
"due_date": 1000000000,
|
"due_date": 1000000000,
|
||||||
"ending_balance": 0,
|
"ending_balance": 0,
|
||||||
|
"finalized_at": 1000000000,
|
||||||
"footer": null,
|
"footer": null,
|
||||||
"hosted_invoice_url": "https://pay.stripe.com/invoice/invst_NORMALIZED0000000000000002",
|
"hosted_invoice_url": "https://pay.stripe.com/invoice/invst_NORMALIZED0000000000000002",
|
||||||
"id": "in_NORMALIZED00000000000002",
|
"id": "in_NORMALIZED00000000000002",
|
||||||
@@ -56,8 +45,6 @@
|
|||||||
"proration": false,
|
"proration": false,
|
||||||
"quantity": 1,
|
"quantity": 1,
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"tax_amounts": [],
|
|
||||||
"tax_rates": [],
|
|
||||||
"type": "invoiceitem"
|
"type": "invoiceitem"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -72,61 +59,40 @@
|
|||||||
"number": "NORMALI-0002",
|
"number": "NORMALI-0002",
|
||||||
"object": "invoice",
|
"object": "invoice",
|
||||||
"paid": false,
|
"paid": false,
|
||||||
"payment_intent": "pi_1F96B2Gh0CmXqmnwZLKqLOFy",
|
|
||||||
"period_end": 1000000000,
|
"period_end": 1000000000,
|
||||||
"period_start": 1000000000,
|
"period_start": 1000000000,
|
||||||
"post_payment_credit_notes_amount": 0,
|
|
||||||
"pre_payment_credit_notes_amount": 0,
|
|
||||||
"receipt_number": null,
|
"receipt_number": null,
|
||||||
"starting_balance": 0,
|
"starting_balance": 0,
|
||||||
"statement_descriptor": "Zulip Standard",
|
"statement_descriptor": "Zulip Standard",
|
||||||
"status": "open",
|
"status": "open",
|
||||||
"status_transitions": {
|
|
||||||
"finalized_at": 1000000000,
|
|
||||||
"marked_uncollectible_at": null,
|
|
||||||
"paid_at": null,
|
|
||||||
"voided_at": null
|
|
||||||
},
|
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"subtotal": 100,
|
"subtotal": 100,
|
||||||
"tax": null,
|
"tax": 0,
|
||||||
"tax_percent": null,
|
"tax_percent": null,
|
||||||
"total": 100,
|
"total": 100,
|
||||||
"total_tax_amounts": [],
|
|
||||||
"webhooks_delivered_at": 1000000000
|
"webhooks_delivered_at": 1000000000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"account_country": "US",
|
|
||||||
"account_name": "Dev account",
|
|
||||||
"amount_due": 984000,
|
"amount_due": 984000,
|
||||||
"amount_paid": 0,
|
"amount_paid": 0,
|
||||||
"amount_remaining": 984000,
|
"amount_remaining": 984000,
|
||||||
"application_fee_amount": null,
|
"application_fee": null,
|
||||||
"attempt_count": 0,
|
"attempt_count": 0,
|
||||||
"attempted": false,
|
"attempted": false,
|
||||||
"auto_advance": true,
|
"auto_advance": true,
|
||||||
"billing": "send_invoice",
|
"billing": "send_invoice",
|
||||||
"billing_reason": "manual",
|
"billing_reason": "manual",
|
||||||
"charge": null,
|
"charge": null,
|
||||||
"collection_method": "send_invoice",
|
|
||||||
"created": 1000000000,
|
|
||||||
"currency": "usd",
|
"currency": "usd",
|
||||||
"custom_fields": null,
|
"custom_fields": null,
|
||||||
"customer": "cus_NORMALIZED0001",
|
"customer": "cus_NORMALIZED0001",
|
||||||
"customer_address": null,
|
"date": 1000000000,
|
||||||
"customer_email": "hamlet@zulip.com",
|
|
||||||
"customer_name": null,
|
|
||||||
"customer_phone": null,
|
|
||||||
"customer_shipping": null,
|
|
||||||
"customer_tax_exempt": "none",
|
|
||||||
"customer_tax_ids": [],
|
|
||||||
"default_payment_method": null,
|
|
||||||
"default_source": null,
|
"default_source": null,
|
||||||
"default_tax_rates": [],
|
|
||||||
"description": "",
|
"description": "",
|
||||||
"discount": null,
|
"discount": null,
|
||||||
"due_date": 1000000000,
|
"due_date": 1000000000,
|
||||||
"ending_balance": 0,
|
"ending_balance": 0,
|
||||||
|
"finalized_at": 1000000000,
|
||||||
"footer": null,
|
"footer": null,
|
||||||
"hosted_invoice_url": "https://pay.stripe.com/invoice/invst_NORMALIZED0000000000000001",
|
"hosted_invoice_url": "https://pay.stripe.com/invoice/invst_NORMALIZED0000000000000001",
|
||||||
"id": "in_NORMALIZED00000000000001",
|
"id": "in_NORMALIZED00000000000001",
|
||||||
@@ -151,8 +117,6 @@
|
|||||||
"proration": false,
|
"proration": false,
|
||||||
"quantity": 123,
|
"quantity": 123,
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"tax_amounts": [],
|
|
||||||
"tax_rates": [],
|
|
||||||
"type": "invoiceitem"
|
"type": "invoiceitem"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -167,27 +131,17 @@
|
|||||||
"number": "NORMALI-0001",
|
"number": "NORMALI-0001",
|
||||||
"object": "invoice",
|
"object": "invoice",
|
||||||
"paid": false,
|
"paid": false,
|
||||||
"payment_intent": "pi_1F96B0Gh0CmXqmnwFFVAGxWQ",
|
|
||||||
"period_end": 1000000000,
|
"period_end": 1000000000,
|
||||||
"period_start": 1000000000,
|
"period_start": 1000000000,
|
||||||
"post_payment_credit_notes_amount": 0,
|
|
||||||
"pre_payment_credit_notes_amount": 0,
|
|
||||||
"receipt_number": null,
|
"receipt_number": null,
|
||||||
"starting_balance": 0,
|
"starting_balance": 0,
|
||||||
"statement_descriptor": "Zulip Standard",
|
"statement_descriptor": "Zulip Standard",
|
||||||
"status": "open",
|
"status": "open",
|
||||||
"status_transitions": {
|
|
||||||
"finalized_at": 1000000000,
|
|
||||||
"marked_uncollectible_at": null,
|
|
||||||
"paid_at": null,
|
|
||||||
"voided_at": null
|
|
||||||
},
|
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"subtotal": 984000,
|
"subtotal": 984000,
|
||||||
"tax": null,
|
"tax": 0,
|
||||||
"tax_percent": null,
|
"tax_percent": null,
|
||||||
"total": 984000,
|
"total": 984000,
|
||||||
"total_tax_amounts": [],
|
|
||||||
"webhooks_delivered_at": 1000000000
|
"webhooks_delivered_at": 1000000000
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -18,6 +18,5 @@
|
|||||||
"proration": false,
|
"proration": false,
|
||||||
"quantity": 123,
|
"quantity": 123,
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"tax_rates": [],
|
|
||||||
"unit_amount": 8000
|
"unit_amount": 8000
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,5 @@
|
|||||||
"proration": false,
|
"proration": false,
|
||||||
"quantity": 1,
|
"quantity": 1,
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"tax_rates": [],
|
|
||||||
"unit_amount": 100
|
"unit_amount": 100
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,19 +5,6 @@
|
|||||||
"application_fee": null,
|
"application_fee": null,
|
||||||
"application_fee_amount": null,
|
"application_fee_amount": null,
|
||||||
"balance_transaction": "txn_NORMALIZED00000000000001",
|
"balance_transaction": "txn_NORMALIZED00000000000001",
|
||||||
"billing_details": {
|
|
||||||
"address": {
|
|
||||||
"city": "Pacific",
|
|
||||||
"country": "United States",
|
|
||||||
"line1": "Under the sea,",
|
|
||||||
"line2": null,
|
|
||||||
"postal_code": "33333",
|
|
||||||
"state": null
|
|
||||||
},
|
|
||||||
"email": null,
|
|
||||||
"name": "Ada Starr",
|
|
||||||
"phone": null
|
|
||||||
},
|
|
||||||
"captured": true,
|
"captured": true,
|
||||||
"created": 1000000000,
|
"created": 1000000000,
|
||||||
"currency": "usd",
|
"currency": "usd",
|
||||||
@@ -45,26 +32,6 @@
|
|||||||
},
|
},
|
||||||
"paid": true,
|
"paid": true,
|
||||||
"payment_intent": null,
|
"payment_intent": null,
|
||||||
"payment_method": "card_NORMALIZED00000000000001",
|
|
||||||
"payment_method_details": {
|
|
||||||
"card": {
|
|
||||||
"brand": "visa",
|
|
||||||
"checks": {
|
|
||||||
"address_line1_check": "pass",
|
|
||||||
"address_postal_code_check": "pass",
|
|
||||||
"cvc_check": "pass"
|
|
||||||
},
|
|
||||||
"country": "US",
|
|
||||||
"exp_month": 3,
|
|
||||||
"exp_year": 2033,
|
|
||||||
"fingerprint": "NORMALIZED000001",
|
|
||||||
"funding": "credit",
|
|
||||||
"last4": "4242",
|
|
||||||
"three_d_secure": null,
|
|
||||||
"wallet": null
|
|
||||||
},
|
|
||||||
"type": "card"
|
|
||||||
},
|
|
||||||
"receipt_email": "hamlet@zulip.com",
|
"receipt_email": "hamlet@zulip.com",
|
||||||
"receipt_number": null,
|
"receipt_number": null,
|
||||||
"receipt_url": "https://pay.stripe.com/receipts/acct_NORMALIZED000001/ch_NORMALIZED00000000000001/rcpt_NORMALIZED000000000000000000001",
|
"receipt_url": "https://pay.stripe.com/receipts/acct_NORMALIZED000001/ch_NORMALIZED00000000000001/rcpt_NORMALIZED000000000000000000001",
|
||||||
@@ -105,7 +72,6 @@
|
|||||||
},
|
},
|
||||||
"source_transfer": null,
|
"source_transfer": null,
|
||||||
"statement_descriptor": "Zulip Standard",
|
"statement_descriptor": "Zulip Standard",
|
||||||
"statement_descriptor_suffix": "Zulip Standard",
|
|
||||||
"status": "succeeded",
|
"status": "succeeded",
|
||||||
"transfer_data": null,
|
"transfer_data": null,
|
||||||
"transfer_group": null
|
"transfer_group": null
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
{
|
{
|
||||||
"account_balance": 0,
|
"account_balance": 0,
|
||||||
"address": null,
|
|
||||||
"balance": 0,
|
|
||||||
"created": 1000000000,
|
"created": 1000000000,
|
||||||
"currency": null,
|
"currency": null,
|
||||||
"default_source": "card_NORMALIZED00000000000001",
|
"default_source": "card_NORMALIZED00000000000001",
|
||||||
@@ -13,7 +11,6 @@
|
|||||||
"invoice_prefix": "NORMA01",
|
"invoice_prefix": "NORMA01",
|
||||||
"invoice_settings": {
|
"invoice_settings": {
|
||||||
"custom_fields": null,
|
"custom_fields": null,
|
||||||
"default_payment_method": null,
|
|
||||||
"footer": null
|
"footer": null
|
||||||
},
|
},
|
||||||
"livemode": false,
|
"livemode": false,
|
||||||
@@ -21,10 +18,7 @@
|
|||||||
"realm_id": "1",
|
"realm_id": "1",
|
||||||
"realm_str": "zulip"
|
"realm_str": "zulip"
|
||||||
},
|
},
|
||||||
"name": null,
|
|
||||||
"object": "customer",
|
"object": "customer",
|
||||||
"phone": null,
|
|
||||||
"preferred_locales": [],
|
|
||||||
"shipping": null,
|
"shipping": null,
|
||||||
"sources": {
|
"sources": {
|
||||||
"data": [
|
"data": [
|
||||||
@@ -66,14 +60,6 @@
|
|||||||
"total_count": 0,
|
"total_count": 0,
|
||||||
"url": "/v1/customers/cus_NORMALIZED0001/subscriptions"
|
"url": "/v1/customers/cus_NORMALIZED0001/subscriptions"
|
||||||
},
|
},
|
||||||
"tax_exempt": "none",
|
|
||||||
"tax_ids": {
|
|
||||||
"data": [],
|
|
||||||
"has_more": false,
|
|
||||||
"object": "list",
|
|
||||||
"total_count": 0,
|
|
||||||
"url": "/v1/customers/cus_NORMALIZED0001/tax_ids"
|
|
||||||
},
|
|
||||||
"tax_info": null,
|
"tax_info": null,
|
||||||
"tax_info_verification": null
|
"tax_info_verification": null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,35 +1,24 @@
|
|||||||
{
|
{
|
||||||
"account_country": "US",
|
|
||||||
"account_name": "Dev account",
|
|
||||||
"amount_due": 0,
|
"amount_due": 0,
|
||||||
"amount_paid": 0,
|
"amount_paid": 0,
|
||||||
"amount_remaining": 0,
|
"amount_remaining": 0,
|
||||||
"application_fee_amount": null,
|
"application_fee": null,
|
||||||
"attempt_count": 0,
|
"attempt_count": 0,
|
||||||
"attempted": false,
|
"attempted": false,
|
||||||
"auto_advance": true,
|
"auto_advance": true,
|
||||||
"billing": "charge_automatically",
|
"billing": "charge_automatically",
|
||||||
"billing_reason": "manual",
|
"billing_reason": "manual",
|
||||||
"charge": null,
|
"charge": null,
|
||||||
"collection_method": "charge_automatically",
|
|
||||||
"created": 1000000000,
|
|
||||||
"currency": "usd",
|
"currency": "usd",
|
||||||
"custom_fields": null,
|
"custom_fields": null,
|
||||||
"customer": "cus_NORMALIZED0001",
|
"customer": "cus_NORMALIZED0001",
|
||||||
"customer_address": null,
|
"date": 1000000000,
|
||||||
"customer_email": "hamlet@zulip.com",
|
|
||||||
"customer_name": null,
|
|
||||||
"customer_phone": null,
|
|
||||||
"customer_shipping": null,
|
|
||||||
"customer_tax_exempt": "none",
|
|
||||||
"customer_tax_ids": [],
|
|
||||||
"default_payment_method": null,
|
|
||||||
"default_source": null,
|
"default_source": null,
|
||||||
"default_tax_rates": [],
|
|
||||||
"description": "",
|
"description": "",
|
||||||
"discount": null,
|
"discount": null,
|
||||||
"due_date": null,
|
"due_date": null,
|
||||||
"ending_balance": null,
|
"ending_balance": null,
|
||||||
|
"finalized_at": null,
|
||||||
"footer": null,
|
"footer": null,
|
||||||
"hosted_invoice_url": null,
|
"hosted_invoice_url": null,
|
||||||
"id": "in_NORMALIZED00000000000001",
|
"id": "in_NORMALIZED00000000000001",
|
||||||
@@ -54,8 +43,6 @@
|
|||||||
"proration": false,
|
"proration": false,
|
||||||
"quantity": 6,
|
"quantity": 6,
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"tax_amounts": [],
|
|
||||||
"tax_rates": [],
|
|
||||||
"type": "invoiceitem"
|
"type": "invoiceitem"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -76,8 +63,6 @@
|
|||||||
"proration": false,
|
"proration": false,
|
||||||
"quantity": 1,
|
"quantity": 1,
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"tax_amounts": [],
|
|
||||||
"tax_rates": [],
|
|
||||||
"type": "invoiceitem"
|
"type": "invoiceitem"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -92,26 +77,16 @@
|
|||||||
"number": "NORMALI-0001",
|
"number": "NORMALI-0001",
|
||||||
"object": "invoice",
|
"object": "invoice",
|
||||||
"paid": false,
|
"paid": false,
|
||||||
"payment_intent": null,
|
|
||||||
"period_end": 1000000000,
|
"period_end": 1000000000,
|
||||||
"period_start": 1000000000,
|
"period_start": 1000000000,
|
||||||
"post_payment_credit_notes_amount": 0,
|
|
||||||
"pre_payment_credit_notes_amount": 0,
|
|
||||||
"receipt_number": null,
|
"receipt_number": null,
|
||||||
"starting_balance": 0,
|
"starting_balance": 0,
|
||||||
"statement_descriptor": "Zulip Standard",
|
"statement_descriptor": "Zulip Standard",
|
||||||
"status": "draft",
|
"status": "draft",
|
||||||
"status_transitions": {
|
|
||||||
"finalized_at": null,
|
|
||||||
"marked_uncollectible_at": null,
|
|
||||||
"paid_at": null,
|
|
||||||
"voided_at": null
|
|
||||||
},
|
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"subtotal": 0,
|
"subtotal": 0,
|
||||||
"tax": null,
|
"tax": 0,
|
||||||
"tax_percent": null,
|
"tax_percent": null,
|
||||||
"total": 0,
|
"total": 0,
|
||||||
"total_tax_amounts": [],
|
|
||||||
"webhooks_delivered_at": null
|
"webhooks_delivered_at": null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,35 +1,24 @@
|
|||||||
{
|
{
|
||||||
"account_country": "US",
|
|
||||||
"account_name": "Dev account",
|
|
||||||
"amount_due": 80697,
|
"amount_due": 80697,
|
||||||
"amount_paid": 0,
|
"amount_paid": 0,
|
||||||
"amount_remaining": 80697,
|
"amount_remaining": 80697,
|
||||||
"application_fee_amount": null,
|
"application_fee": null,
|
||||||
"attempt_count": 0,
|
"attempt_count": 0,
|
||||||
"attempted": false,
|
"attempted": false,
|
||||||
"auto_advance": true,
|
"auto_advance": true,
|
||||||
"billing": "charge_automatically",
|
"billing": "charge_automatically",
|
||||||
"billing_reason": "manual",
|
"billing_reason": "manual",
|
||||||
"charge": null,
|
"charge": null,
|
||||||
"collection_method": "charge_automatically",
|
|
||||||
"created": 1000000000,
|
|
||||||
"currency": "usd",
|
"currency": "usd",
|
||||||
"custom_fields": null,
|
"custom_fields": null,
|
||||||
"customer": "cus_NORMALIZED0001",
|
"customer": "cus_NORMALIZED0001",
|
||||||
"customer_address": null,
|
"date": 1000000000,
|
||||||
"customer_email": "hamlet@zulip.com",
|
|
||||||
"customer_name": null,
|
|
||||||
"customer_phone": null,
|
|
||||||
"customer_shipping": null,
|
|
||||||
"customer_tax_exempt": "none",
|
|
||||||
"customer_tax_ids": [],
|
|
||||||
"default_payment_method": null,
|
|
||||||
"default_source": null,
|
"default_source": null,
|
||||||
"default_tax_rates": [],
|
|
||||||
"description": "",
|
"description": "",
|
||||||
"discount": null,
|
"discount": null,
|
||||||
"due_date": null,
|
"due_date": null,
|
||||||
"ending_balance": null,
|
"ending_balance": null,
|
||||||
|
"finalized_at": null,
|
||||||
"footer": null,
|
"footer": null,
|
||||||
"hosted_invoice_url": null,
|
"hosted_invoice_url": null,
|
||||||
"id": "in_NORMALIZED00000000000002",
|
"id": "in_NORMALIZED00000000000002",
|
||||||
@@ -54,8 +43,6 @@
|
|||||||
"proration": false,
|
"proration": false,
|
||||||
"quantity": 1,
|
"quantity": 1,
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"tax_amounts": [],
|
|
||||||
"tax_rates": [],
|
|
||||||
"type": "invoiceitem"
|
"type": "invoiceitem"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -76,8 +63,6 @@
|
|||||||
"proration": false,
|
"proration": false,
|
||||||
"quantity": 7,
|
"quantity": 7,
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"tax_amounts": [],
|
|
||||||
"tax_rates": [],
|
|
||||||
"type": "invoiceitem"
|
"type": "invoiceitem"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -98,8 +83,6 @@
|
|||||||
"proration": false,
|
"proration": false,
|
||||||
"quantity": 3,
|
"quantity": 3,
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"tax_amounts": [],
|
|
||||||
"tax_rates": [],
|
|
||||||
"type": "invoiceitem"
|
"type": "invoiceitem"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -114,26 +97,16 @@
|
|||||||
"number": "NORMALI-0002",
|
"number": "NORMALI-0002",
|
||||||
"object": "invoice",
|
"object": "invoice",
|
||||||
"paid": false,
|
"paid": false,
|
||||||
"payment_intent": null,
|
|
||||||
"period_end": 1000000000,
|
"period_end": 1000000000,
|
||||||
"period_start": 1000000000,
|
"period_start": 1000000000,
|
||||||
"post_payment_credit_notes_amount": 0,
|
|
||||||
"pre_payment_credit_notes_amount": 0,
|
|
||||||
"receipt_number": null,
|
"receipt_number": null,
|
||||||
"starting_balance": 0,
|
"starting_balance": 0,
|
||||||
"statement_descriptor": "Zulip Standard",
|
"statement_descriptor": "Zulip Standard",
|
||||||
"status": "draft",
|
"status": "draft",
|
||||||
"status_transitions": {
|
|
||||||
"finalized_at": null,
|
|
||||||
"marked_uncollectible_at": null,
|
|
||||||
"paid_at": null,
|
|
||||||
"voided_at": null
|
|
||||||
},
|
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"subtotal": 80697,
|
"subtotal": 80697,
|
||||||
"tax": null,
|
"tax": 0,
|
||||||
"tax_percent": null,
|
"tax_percent": null,
|
||||||
"total": 80697,
|
"total": 80697,
|
||||||
"total_tax_amounts": [],
|
|
||||||
"webhooks_delivered_at": null
|
"webhooks_delivered_at": null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,35 +1,24 @@
|
|||||||
{
|
{
|
||||||
"account_country": "US",
|
|
||||||
"account_name": "Dev account",
|
|
||||||
"amount_due": 0,
|
"amount_due": 0,
|
||||||
"amount_paid": 0,
|
"amount_paid": 0,
|
||||||
"amount_remaining": 0,
|
"amount_remaining": 0,
|
||||||
"application_fee_amount": null,
|
"application_fee": null,
|
||||||
"attempt_count": 0,
|
"attempt_count": 0,
|
||||||
"attempted": true,
|
"attempted": true,
|
||||||
"auto_advance": false,
|
"auto_advance": false,
|
||||||
"billing": "charge_automatically",
|
"billing": "charge_automatically",
|
||||||
"billing_reason": "manual",
|
"billing_reason": "manual",
|
||||||
"charge": null,
|
"charge": null,
|
||||||
"collection_method": "charge_automatically",
|
|
||||||
"created": 1000000000,
|
|
||||||
"currency": "usd",
|
"currency": "usd",
|
||||||
"custom_fields": null,
|
"custom_fields": null,
|
||||||
"customer": "cus_NORMALIZED0001",
|
"customer": "cus_NORMALIZED0001",
|
||||||
"customer_address": null,
|
"date": 1000000000,
|
||||||
"customer_email": "hamlet@zulip.com",
|
|
||||||
"customer_name": null,
|
|
||||||
"customer_phone": null,
|
|
||||||
"customer_shipping": null,
|
|
||||||
"customer_tax_exempt": "none",
|
|
||||||
"customer_tax_ids": [],
|
|
||||||
"default_payment_method": null,
|
|
||||||
"default_source": null,
|
"default_source": null,
|
||||||
"default_tax_rates": [],
|
|
||||||
"description": "",
|
"description": "",
|
||||||
"discount": null,
|
"discount": null,
|
||||||
"due_date": 1000000000,
|
"due_date": 1000000000,
|
||||||
"ending_balance": 0,
|
"ending_balance": 0,
|
||||||
|
"finalized_at": 1000000000,
|
||||||
"footer": null,
|
"footer": null,
|
||||||
"hosted_invoice_url": "https://pay.stripe.com/invoice/invst_NORMALIZED0000000000000001",
|
"hosted_invoice_url": "https://pay.stripe.com/invoice/invst_NORMALIZED0000000000000001",
|
||||||
"id": "in_NORMALIZED00000000000001",
|
"id": "in_NORMALIZED00000000000001",
|
||||||
@@ -54,8 +43,6 @@
|
|||||||
"proration": false,
|
"proration": false,
|
||||||
"quantity": 6,
|
"quantity": 6,
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"tax_amounts": [],
|
|
||||||
"tax_rates": [],
|
|
||||||
"type": "invoiceitem"
|
"type": "invoiceitem"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -76,8 +63,6 @@
|
|||||||
"proration": false,
|
"proration": false,
|
||||||
"quantity": 1,
|
"quantity": 1,
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"tax_amounts": [],
|
|
||||||
"tax_rates": [],
|
|
||||||
"type": "invoiceitem"
|
"type": "invoiceitem"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -92,26 +77,16 @@
|
|||||||
"number": "NORMALI-0001",
|
"number": "NORMALI-0001",
|
||||||
"object": "invoice",
|
"object": "invoice",
|
||||||
"paid": true,
|
"paid": true,
|
||||||
"payment_intent": null,
|
|
||||||
"period_end": 1000000000,
|
"period_end": 1000000000,
|
||||||
"period_start": 1000000000,
|
"period_start": 1000000000,
|
||||||
"post_payment_credit_notes_amount": 0,
|
|
||||||
"pre_payment_credit_notes_amount": 0,
|
|
||||||
"receipt_number": null,
|
"receipt_number": null,
|
||||||
"starting_balance": 0,
|
"starting_balance": 0,
|
||||||
"statement_descriptor": "Zulip Standard",
|
"statement_descriptor": "Zulip Standard",
|
||||||
"status": "paid",
|
"status": "paid",
|
||||||
"status_transitions": {
|
|
||||||
"finalized_at": 1000000000,
|
|
||||||
"marked_uncollectible_at": null,
|
|
||||||
"paid_at": 1000000000,
|
|
||||||
"voided_at": null
|
|
||||||
},
|
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"subtotal": 0,
|
"subtotal": 0,
|
||||||
"tax": null,
|
"tax": 0,
|
||||||
"tax_percent": null,
|
"tax_percent": null,
|
||||||
"total": 0,
|
"total": 0,
|
||||||
"total_tax_amounts": [],
|
|
||||||
"webhooks_delivered_at": 1000000000
|
"webhooks_delivered_at": 1000000000
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,35 +1,24 @@
|
|||||||
{
|
{
|
||||||
"account_country": "US",
|
|
||||||
"account_name": "Dev account",
|
|
||||||
"amount_due": 80697,
|
"amount_due": 80697,
|
||||||
"amount_paid": 0,
|
"amount_paid": 0,
|
||||||
"amount_remaining": 80697,
|
"amount_remaining": 80697,
|
||||||
"application_fee_amount": null,
|
"application_fee": null,
|
||||||
"attempt_count": 0,
|
"attempt_count": 0,
|
||||||
"attempted": false,
|
"attempted": false,
|
||||||
"auto_advance": true,
|
"auto_advance": true,
|
||||||
"billing": "charge_automatically",
|
"billing": "charge_automatically",
|
||||||
"billing_reason": "manual",
|
"billing_reason": "manual",
|
||||||
"charge": null,
|
"charge": null,
|
||||||
"collection_method": "charge_automatically",
|
|
||||||
"created": 1000000000,
|
|
||||||
"currency": "usd",
|
"currency": "usd",
|
||||||
"custom_fields": null,
|
"custom_fields": null,
|
||||||
"customer": "cus_NORMALIZED0001",
|
"customer": "cus_NORMALIZED0001",
|
||||||
"customer_address": null,
|
"date": 1000000000,
|
||||||
"customer_email": "hamlet@zulip.com",
|
|
||||||
"customer_name": null,
|
|
||||||
"customer_phone": null,
|
|
||||||
"customer_shipping": null,
|
|
||||||
"customer_tax_exempt": "none",
|
|
||||||
"customer_tax_ids": [],
|
|
||||||
"default_payment_method": null,
|
|
||||||
"default_source": null,
|
"default_source": null,
|
||||||
"default_tax_rates": [],
|
|
||||||
"description": "",
|
"description": "",
|
||||||
"discount": null,
|
"discount": null,
|
||||||
"due_date": 1000000000,
|
"due_date": 1000000000,
|
||||||
"ending_balance": 0,
|
"ending_balance": 0,
|
||||||
|
"finalized_at": 1000000000,
|
||||||
"footer": null,
|
"footer": null,
|
||||||
"hosted_invoice_url": "https://pay.stripe.com/invoice/invst_NORMALIZED0000000000000002",
|
"hosted_invoice_url": "https://pay.stripe.com/invoice/invst_NORMALIZED0000000000000002",
|
||||||
"id": "in_NORMALIZED00000000000002",
|
"id": "in_NORMALIZED00000000000002",
|
||||||
@@ -54,8 +43,6 @@
|
|||||||
"proration": false,
|
"proration": false,
|
||||||
"quantity": 1,
|
"quantity": 1,
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"tax_amounts": [],
|
|
||||||
"tax_rates": [],
|
|
||||||
"type": "invoiceitem"
|
"type": "invoiceitem"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -76,8 +63,6 @@
|
|||||||
"proration": false,
|
"proration": false,
|
||||||
"quantity": 7,
|
"quantity": 7,
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"tax_amounts": [],
|
|
||||||
"tax_rates": [],
|
|
||||||
"type": "invoiceitem"
|
"type": "invoiceitem"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -98,8 +83,6 @@
|
|||||||
"proration": false,
|
"proration": false,
|
||||||
"quantity": 3,
|
"quantity": 3,
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"tax_amounts": [],
|
|
||||||
"tax_rates": [],
|
|
||||||
"type": "invoiceitem"
|
"type": "invoiceitem"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -114,26 +97,16 @@
|
|||||||
"number": "NORMALI-0002",
|
"number": "NORMALI-0002",
|
||||||
"object": "invoice",
|
"object": "invoice",
|
||||||
"paid": false,
|
"paid": false,
|
||||||
"payment_intent": "pi_1F96BCGh0CmXqmnwfafGafrD",
|
|
||||||
"period_end": 1000000000,
|
"period_end": 1000000000,
|
||||||
"period_start": 1000000000,
|
"period_start": 1000000000,
|
||||||
"post_payment_credit_notes_amount": 0,
|
|
||||||
"pre_payment_credit_notes_amount": 0,
|
|
||||||
"receipt_number": null,
|
"receipt_number": null,
|
||||||
"starting_balance": 0,
|
"starting_balance": 0,
|
||||||
"statement_descriptor": "Zulip Standard",
|
"statement_descriptor": "Zulip Standard",
|
||||||
"status": "open",
|
"status": "open",
|
||||||
"status_transitions": {
|
|
||||||
"finalized_at": 1000000000,
|
|
||||||
"marked_uncollectible_at": null,
|
|
||||||
"paid_at": null,
|
|
||||||
"voided_at": null
|
|
||||||
},
|
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"subtotal": 80697,
|
"subtotal": 80697,
|
||||||
"tax": null,
|
"tax": 0,
|
||||||
"tax_percent": null,
|
"tax_percent": null,
|
||||||
"total": 80697,
|
"total": 80697,
|
||||||
"total_tax_amounts": [],
|
|
||||||
"webhooks_delivered_at": 1000000000
|
"webhooks_delivered_at": 1000000000
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,37 +1,26 @@
|
|||||||
{
|
{
|
||||||
"data": [
|
"data": [
|
||||||
{
|
{
|
||||||
"account_country": "US",
|
|
||||||
"account_name": "Dev account",
|
|
||||||
"amount_due": 80697,
|
"amount_due": 80697,
|
||||||
"amount_paid": 0,
|
"amount_paid": 0,
|
||||||
"amount_remaining": 80697,
|
"amount_remaining": 80697,
|
||||||
"application_fee_amount": null,
|
"application_fee": null,
|
||||||
"attempt_count": 0,
|
"attempt_count": 0,
|
||||||
"attempted": false,
|
"attempted": false,
|
||||||
"auto_advance": true,
|
"auto_advance": true,
|
||||||
"billing": "charge_automatically",
|
"billing": "charge_automatically",
|
||||||
"billing_reason": "manual",
|
"billing_reason": "manual",
|
||||||
"charge": null,
|
"charge": null,
|
||||||
"collection_method": "charge_automatically",
|
|
||||||
"created": 1000000000,
|
|
||||||
"currency": "usd",
|
"currency": "usd",
|
||||||
"custom_fields": null,
|
"custom_fields": null,
|
||||||
"customer": "cus_NORMALIZED0001",
|
"customer": "cus_NORMALIZED0001",
|
||||||
"customer_address": null,
|
"date": 1000000000,
|
||||||
"customer_email": "hamlet@zulip.com",
|
|
||||||
"customer_name": null,
|
|
||||||
"customer_phone": null,
|
|
||||||
"customer_shipping": null,
|
|
||||||
"customer_tax_exempt": "none",
|
|
||||||
"customer_tax_ids": [],
|
|
||||||
"default_payment_method": null,
|
|
||||||
"default_source": null,
|
"default_source": null,
|
||||||
"default_tax_rates": [],
|
|
||||||
"description": "",
|
"description": "",
|
||||||
"discount": null,
|
"discount": null,
|
||||||
"due_date": 1000000000,
|
"due_date": 1000000000,
|
||||||
"ending_balance": 0,
|
"ending_balance": 0,
|
||||||
|
"finalized_at": 1000000000,
|
||||||
"footer": null,
|
"footer": null,
|
||||||
"hosted_invoice_url": "https://pay.stripe.com/invoice/invst_NORMALIZED0000000000000002",
|
"hosted_invoice_url": "https://pay.stripe.com/invoice/invst_NORMALIZED0000000000000002",
|
||||||
"id": "in_NORMALIZED00000000000002",
|
"id": "in_NORMALIZED00000000000002",
|
||||||
@@ -56,8 +45,6 @@
|
|||||||
"proration": false,
|
"proration": false,
|
||||||
"quantity": 1,
|
"quantity": 1,
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"tax_amounts": [],
|
|
||||||
"tax_rates": [],
|
|
||||||
"type": "invoiceitem"
|
"type": "invoiceitem"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -78,8 +65,6 @@
|
|||||||
"proration": false,
|
"proration": false,
|
||||||
"quantity": 7,
|
"quantity": 7,
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"tax_amounts": [],
|
|
||||||
"tax_rates": [],
|
|
||||||
"type": "invoiceitem"
|
"type": "invoiceitem"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -100,8 +85,6 @@
|
|||||||
"proration": false,
|
"proration": false,
|
||||||
"quantity": 3,
|
"quantity": 3,
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"tax_amounts": [],
|
|
||||||
"tax_rates": [],
|
|
||||||
"type": "invoiceitem"
|
"type": "invoiceitem"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -116,61 +99,40 @@
|
|||||||
"number": "NORMALI-0002",
|
"number": "NORMALI-0002",
|
||||||
"object": "invoice",
|
"object": "invoice",
|
||||||
"paid": false,
|
"paid": false,
|
||||||
"payment_intent": "pi_1F96BCGh0CmXqmnwfafGafrD",
|
|
||||||
"period_end": 1000000000,
|
"period_end": 1000000000,
|
||||||
"period_start": 1000000000,
|
"period_start": 1000000000,
|
||||||
"post_payment_credit_notes_amount": 0,
|
|
||||||
"pre_payment_credit_notes_amount": 0,
|
|
||||||
"receipt_number": null,
|
"receipt_number": null,
|
||||||
"starting_balance": 0,
|
"starting_balance": 0,
|
||||||
"statement_descriptor": "Zulip Standard",
|
"statement_descriptor": "Zulip Standard",
|
||||||
"status": "open",
|
"status": "open",
|
||||||
"status_transitions": {
|
|
||||||
"finalized_at": 1000000000,
|
|
||||||
"marked_uncollectible_at": null,
|
|
||||||
"paid_at": null,
|
|
||||||
"voided_at": null
|
|
||||||
},
|
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"subtotal": 80697,
|
"subtotal": 80697,
|
||||||
"tax": null,
|
"tax": 0,
|
||||||
"tax_percent": null,
|
"tax_percent": null,
|
||||||
"total": 80697,
|
"total": 80697,
|
||||||
"total_tax_amounts": [],
|
|
||||||
"webhooks_delivered_at": 1000000000
|
"webhooks_delivered_at": 1000000000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"account_country": "US",
|
|
||||||
"account_name": "Dev account",
|
|
||||||
"amount_due": 0,
|
"amount_due": 0,
|
||||||
"amount_paid": 0,
|
"amount_paid": 0,
|
||||||
"amount_remaining": 0,
|
"amount_remaining": 0,
|
||||||
"application_fee_amount": null,
|
"application_fee": null,
|
||||||
"attempt_count": 0,
|
"attempt_count": 0,
|
||||||
"attempted": true,
|
"attempted": true,
|
||||||
"auto_advance": false,
|
"auto_advance": false,
|
||||||
"billing": "charge_automatically",
|
"billing": "charge_automatically",
|
||||||
"billing_reason": "manual",
|
"billing_reason": "manual",
|
||||||
"charge": null,
|
"charge": null,
|
||||||
"collection_method": "charge_automatically",
|
|
||||||
"created": 1000000000,
|
|
||||||
"currency": "usd",
|
"currency": "usd",
|
||||||
"custom_fields": null,
|
"custom_fields": null,
|
||||||
"customer": "cus_NORMALIZED0001",
|
"customer": "cus_NORMALIZED0001",
|
||||||
"customer_address": null,
|
"date": 1000000000,
|
||||||
"customer_email": "hamlet@zulip.com",
|
|
||||||
"customer_name": null,
|
|
||||||
"customer_phone": null,
|
|
||||||
"customer_shipping": null,
|
|
||||||
"customer_tax_exempt": "none",
|
|
||||||
"customer_tax_ids": [],
|
|
||||||
"default_payment_method": null,
|
|
||||||
"default_source": null,
|
"default_source": null,
|
||||||
"default_tax_rates": [],
|
|
||||||
"description": "",
|
"description": "",
|
||||||
"discount": null,
|
"discount": null,
|
||||||
"due_date": 1000000000,
|
"due_date": 1000000000,
|
||||||
"ending_balance": 0,
|
"ending_balance": 0,
|
||||||
|
"finalized_at": 1000000000,
|
||||||
"footer": null,
|
"footer": null,
|
||||||
"hosted_invoice_url": "https://pay.stripe.com/invoice/invst_NORMALIZED0000000000000001",
|
"hosted_invoice_url": "https://pay.stripe.com/invoice/invst_NORMALIZED0000000000000001",
|
||||||
"id": "in_NORMALIZED00000000000001",
|
"id": "in_NORMALIZED00000000000001",
|
||||||
@@ -195,8 +157,6 @@
|
|||||||
"proration": false,
|
"proration": false,
|
||||||
"quantity": 6,
|
"quantity": 6,
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"tax_amounts": [],
|
|
||||||
"tax_rates": [],
|
|
||||||
"type": "invoiceitem"
|
"type": "invoiceitem"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -217,8 +177,6 @@
|
|||||||
"proration": false,
|
"proration": false,
|
||||||
"quantity": 1,
|
"quantity": 1,
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"tax_amounts": [],
|
|
||||||
"tax_rates": [],
|
|
||||||
"type": "invoiceitem"
|
"type": "invoiceitem"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -233,27 +191,17 @@
|
|||||||
"number": "NORMALI-0001",
|
"number": "NORMALI-0001",
|
||||||
"object": "invoice",
|
"object": "invoice",
|
||||||
"paid": true,
|
"paid": true,
|
||||||
"payment_intent": null,
|
|
||||||
"period_end": 1000000000,
|
"period_end": 1000000000,
|
||||||
"period_start": 1000000000,
|
"period_start": 1000000000,
|
||||||
"post_payment_credit_notes_amount": 0,
|
|
||||||
"pre_payment_credit_notes_amount": 0,
|
|
||||||
"receipt_number": null,
|
"receipt_number": null,
|
||||||
"starting_balance": 0,
|
"starting_balance": 0,
|
||||||
"statement_descriptor": "Zulip Standard",
|
"statement_descriptor": "Zulip Standard",
|
||||||
"status": "paid",
|
"status": "paid",
|
||||||
"status_transitions": {
|
|
||||||
"finalized_at": 1000000000,
|
|
||||||
"marked_uncollectible_at": null,
|
|
||||||
"paid_at": 1000000000,
|
|
||||||
"voided_at": null
|
|
||||||
},
|
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"subtotal": 0,
|
"subtotal": 0,
|
||||||
"tax": null,
|
"tax": 0,
|
||||||
"tax_percent": null,
|
"tax_percent": null,
|
||||||
"total": 0,
|
"total": 0,
|
||||||
"total_tax_amounts": [],
|
|
||||||
"webhooks_delivered_at": 1000000000
|
"webhooks_delivered_at": 1000000000
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -18,6 +18,5 @@
|
|||||||
"proration": false,
|
"proration": false,
|
||||||
"quantity": 1,
|
"quantity": 1,
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"tax_rates": [],
|
|
||||||
"unit_amount": -48000
|
"unit_amount": -48000
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,5 @@
|
|||||||
"proration": false,
|
"proration": false,
|
||||||
"quantity": 6,
|
"quantity": 6,
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"tax_rates": [],
|
|
||||||
"unit_amount": 8000
|
"unit_amount": 8000
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,5 @@
|
|||||||
"proration": false,
|
"proration": false,
|
||||||
"quantity": 3,
|
"quantity": 3,
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"tax_rates": [],
|
|
||||||
"unit_amount": 5814
|
"unit_amount": 5814
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,5 @@
|
|||||||
"proration": false,
|
"proration": false,
|
||||||
"quantity": 7,
|
"quantity": 7,
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"tax_rates": [],
|
|
||||||
"unit_amount": 8000
|
"unit_amount": 8000
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,5 @@
|
|||||||
"proration": false,
|
"proration": false,
|
||||||
"quantity": 1,
|
"quantity": 1,
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"tax_rates": [],
|
|
||||||
"unit_amount": 7255
|
"unit_amount": 7255
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
},
|
},
|
||||||
"livemode": false,
|
"livemode": false,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"realm_id": "2",
|
"realm_id": "1",
|
||||||
"realm_str": "zulip"
|
"realm_str": "zulip"
|
||||||
},
|
},
|
||||||
"object": "customer",
|
"object": "customer",
|
||||||
|
|||||||
@@ -5,19 +5,6 @@
|
|||||||
"application_fee": null,
|
"application_fee": null,
|
||||||
"application_fee_amount": null,
|
"application_fee_amount": null,
|
||||||
"balance_transaction": "txn_NORMALIZED00000000000001",
|
"balance_transaction": "txn_NORMALIZED00000000000001",
|
||||||
"billing_details": {
|
|
||||||
"address": {
|
|
||||||
"city": "Pacific",
|
|
||||||
"country": "United States",
|
|
||||||
"line1": "Under the sea,",
|
|
||||||
"line2": null,
|
|
||||||
"postal_code": "33333",
|
|
||||||
"state": null
|
|
||||||
},
|
|
||||||
"email": null,
|
|
||||||
"name": "Ada Starr",
|
|
||||||
"phone": null
|
|
||||||
},
|
|
||||||
"captured": true,
|
"captured": true,
|
||||||
"created": 1000000000,
|
"created": 1000000000,
|
||||||
"currency": "usd",
|
"currency": "usd",
|
||||||
@@ -45,26 +32,6 @@
|
|||||||
},
|
},
|
||||||
"paid": true,
|
"paid": true,
|
||||||
"payment_intent": null,
|
"payment_intent": null,
|
||||||
"payment_method": "card_NORMALIZED00000000000001",
|
|
||||||
"payment_method_details": {
|
|
||||||
"card": {
|
|
||||||
"brand": "visa",
|
|
||||||
"checks": {
|
|
||||||
"address_line1_check": "pass",
|
|
||||||
"address_postal_code_check": "pass",
|
|
||||||
"cvc_check": "pass"
|
|
||||||
},
|
|
||||||
"country": "US",
|
|
||||||
"exp_month": 3,
|
|
||||||
"exp_year": 2033,
|
|
||||||
"fingerprint": "NORMALIZED000001",
|
|
||||||
"funding": "credit",
|
|
||||||
"last4": "4242",
|
|
||||||
"three_d_secure": null,
|
|
||||||
"wallet": null
|
|
||||||
},
|
|
||||||
"type": "card"
|
|
||||||
},
|
|
||||||
"receipt_email": "hamlet@zulip.com",
|
"receipt_email": "hamlet@zulip.com",
|
||||||
"receipt_number": null,
|
"receipt_number": null,
|
||||||
"receipt_url": "https://pay.stripe.com/receipts/acct_NORMALIZED000001/ch_NORMALIZED00000000000001/rcpt_NORMALIZED000000000000000000001",
|
"receipt_url": "https://pay.stripe.com/receipts/acct_NORMALIZED000001/ch_NORMALIZED00000000000001/rcpt_NORMALIZED000000000000000000001",
|
||||||
@@ -105,7 +72,6 @@
|
|||||||
},
|
},
|
||||||
"source_transfer": null,
|
"source_transfer": null,
|
||||||
"statement_descriptor": "Zulip Standard",
|
"statement_descriptor": "Zulip Standard",
|
||||||
"statement_descriptor_suffix": "Zulip Standard",
|
|
||||||
"status": "succeeded",
|
"status": "succeeded",
|
||||||
"transfer_data": null,
|
"transfer_data": null,
|
||||||
"transfer_group": null
|
"transfer_group": null
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
{
|
{
|
||||||
"account_balance": 0,
|
"account_balance": 0,
|
||||||
"address": null,
|
|
||||||
"balance": 0,
|
|
||||||
"created": 1000000000,
|
"created": 1000000000,
|
||||||
"currency": null,
|
"currency": null,
|
||||||
"default_source": "card_NORMALIZED00000000000001",
|
"default_source": "card_NORMALIZED00000000000001",
|
||||||
@@ -13,7 +11,6 @@
|
|||||||
"invoice_prefix": "NORMA01",
|
"invoice_prefix": "NORMA01",
|
||||||
"invoice_settings": {
|
"invoice_settings": {
|
||||||
"custom_fields": null,
|
"custom_fields": null,
|
||||||
"default_payment_method": null,
|
|
||||||
"footer": null
|
"footer": null
|
||||||
},
|
},
|
||||||
"livemode": false,
|
"livemode": false,
|
||||||
@@ -21,10 +18,7 @@
|
|||||||
"realm_id": "1",
|
"realm_id": "1",
|
||||||
"realm_str": "zulip"
|
"realm_str": "zulip"
|
||||||
},
|
},
|
||||||
"name": null,
|
|
||||||
"object": "customer",
|
"object": "customer",
|
||||||
"phone": null,
|
|
||||||
"preferred_locales": [],
|
|
||||||
"shipping": null,
|
"shipping": null,
|
||||||
"sources": {
|
"sources": {
|
||||||
"data": [
|
"data": [
|
||||||
@@ -66,14 +60,6 @@
|
|||||||
"total_count": 0,
|
"total_count": 0,
|
||||||
"url": "/v1/customers/cus_NORMALIZED0001/subscriptions"
|
"url": "/v1/customers/cus_NORMALIZED0001/subscriptions"
|
||||||
},
|
},
|
||||||
"tax_exempt": "none",
|
|
||||||
"tax_ids": {
|
|
||||||
"data": [],
|
|
||||||
"has_more": false,
|
|
||||||
"object": "list",
|
|
||||||
"total_count": 0,
|
|
||||||
"url": "/v1/customers/cus_NORMALIZED0001/tax_ids"
|
|
||||||
},
|
|
||||||
"tax_info": null,
|
"tax_info": null,
|
||||||
"tax_info_verification": null
|
"tax_info_verification": null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
{
|
{
|
||||||
"account_balance": 0,
|
"account_balance": 0,
|
||||||
"address": null,
|
|
||||||
"balance": 0,
|
|
||||||
"created": 1000000000,
|
"created": 1000000000,
|
||||||
"currency": "usd",
|
"currency": "usd",
|
||||||
"default_source": {
|
"default_source": {
|
||||||
@@ -37,7 +35,6 @@
|
|||||||
"invoice_prefix": "NORMA01",
|
"invoice_prefix": "NORMA01",
|
||||||
"invoice_settings": {
|
"invoice_settings": {
|
||||||
"custom_fields": null,
|
"custom_fields": null,
|
||||||
"default_payment_method": null,
|
|
||||||
"footer": null
|
"footer": null
|
||||||
},
|
},
|
||||||
"livemode": false,
|
"livemode": false,
|
||||||
@@ -45,10 +42,7 @@
|
|||||||
"realm_id": "1",
|
"realm_id": "1",
|
||||||
"realm_str": "zulip"
|
"realm_str": "zulip"
|
||||||
},
|
},
|
||||||
"name": null,
|
|
||||||
"object": "customer",
|
"object": "customer",
|
||||||
"phone": null,
|
|
||||||
"preferred_locales": [],
|
|
||||||
"shipping": null,
|
"shipping": null,
|
||||||
"sources": {
|
"sources": {
|
||||||
"data": [
|
"data": [
|
||||||
@@ -90,14 +84,6 @@
|
|||||||
"total_count": 0,
|
"total_count": 0,
|
||||||
"url": "/v1/customers/cus_NORMALIZED0001/subscriptions"
|
"url": "/v1/customers/cus_NORMALIZED0001/subscriptions"
|
||||||
},
|
},
|
||||||
"tax_exempt": "none",
|
|
||||||
"tax_ids": {
|
|
||||||
"data": [],
|
|
||||||
"has_more": false,
|
|
||||||
"object": "list",
|
|
||||||
"total_count": 0,
|
|
||||||
"url": "/v1/customers/cus_NORMALIZED0001/tax_ids"
|
|
||||||
},
|
|
||||||
"tax_info": null,
|
"tax_info": null,
|
||||||
"tax_info_verification": null
|
"tax_info_verification": null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
{
|
{
|
||||||
"account_balance": 0,
|
"account_balance": 0,
|
||||||
"address": null,
|
|
||||||
"balance": 0,
|
|
||||||
"created": 1000000000,
|
"created": 1000000000,
|
||||||
"currency": "usd",
|
"currency": "usd",
|
||||||
"default_source": {
|
"default_source": {
|
||||||
@@ -13,17 +11,17 @@
|
|||||||
"address_state": null,
|
"address_state": null,
|
||||||
"address_zip": "33333",
|
"address_zip": "33333",
|
||||||
"address_zip_check": "pass",
|
"address_zip_check": "pass",
|
||||||
"brand": "Visa",
|
"brand": "MasterCard",
|
||||||
"country": "US",
|
"country": "US",
|
||||||
"customer": "cus_NORMALIZED0001",
|
"customer": "cus_NORMALIZED0001",
|
||||||
"cvc_check": "pass",
|
"cvc_check": "pass",
|
||||||
"dynamic_last4": null,
|
"dynamic_last4": null,
|
||||||
"exp_month": 3,
|
"exp_month": 3,
|
||||||
"exp_year": 2033,
|
"exp_year": 2033,
|
||||||
"fingerprint": "NORMALIZED000001",
|
"fingerprint": "NORMALIZED000002",
|
||||||
"funding": "credit",
|
"funding": "credit",
|
||||||
"id": "card_NORMALIZED00000000000001",
|
"id": "card_NORMALIZED00000000000002",
|
||||||
"last4": "4242",
|
"last4": "4444",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"name": "Ada Starr",
|
"name": "Ada Starr",
|
||||||
"object": "card",
|
"object": "card",
|
||||||
@@ -37,7 +35,6 @@
|
|||||||
"invoice_prefix": "NORMA01",
|
"invoice_prefix": "NORMA01",
|
||||||
"invoice_settings": {
|
"invoice_settings": {
|
||||||
"custom_fields": null,
|
"custom_fields": null,
|
||||||
"default_payment_method": null,
|
|
||||||
"footer": null
|
"footer": null
|
||||||
},
|
},
|
||||||
"livemode": false,
|
"livemode": false,
|
||||||
@@ -45,10 +42,7 @@
|
|||||||
"realm_id": "1",
|
"realm_id": "1",
|
||||||
"realm_str": "zulip"
|
"realm_str": "zulip"
|
||||||
},
|
},
|
||||||
"name": null,
|
|
||||||
"object": "customer",
|
"object": "customer",
|
||||||
"phone": null,
|
|
||||||
"preferred_locales": [],
|
|
||||||
"shipping": null,
|
"shipping": null,
|
||||||
"sources": {
|
"sources": {
|
||||||
"data": [
|
"data": [
|
||||||
@@ -61,17 +55,17 @@
|
|||||||
"address_state": null,
|
"address_state": null,
|
||||||
"address_zip": "33333",
|
"address_zip": "33333",
|
||||||
"address_zip_check": "pass",
|
"address_zip_check": "pass",
|
||||||
"brand": "Visa",
|
"brand": "MasterCard",
|
||||||
"country": "US",
|
"country": "US",
|
||||||
"customer": "cus_NORMALIZED0001",
|
"customer": "cus_NORMALIZED0001",
|
||||||
"cvc_check": "pass",
|
"cvc_check": "pass",
|
||||||
"dynamic_last4": null,
|
"dynamic_last4": null,
|
||||||
"exp_month": 3,
|
"exp_month": 3,
|
||||||
"exp_year": 2033,
|
"exp_year": 2033,
|
||||||
"fingerprint": "NORMALIZED000001",
|
"fingerprint": "NORMALIZED000002",
|
||||||
"funding": "credit",
|
"funding": "credit",
|
||||||
"id": "card_NORMALIZED00000000000001",
|
"id": "card_NORMALIZED00000000000002",
|
||||||
"last4": "4242",
|
"last4": "4444",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"name": "Ada Starr",
|
"name": "Ada Starr",
|
||||||
"object": "card",
|
"object": "card",
|
||||||
@@ -90,14 +84,6 @@
|
|||||||
"total_count": 0,
|
"total_count": 0,
|
||||||
"url": "/v1/customers/cus_NORMALIZED0001/subscriptions"
|
"url": "/v1/customers/cus_NORMALIZED0001/subscriptions"
|
||||||
},
|
},
|
||||||
"tax_exempt": "none",
|
|
||||||
"tax_ids": {
|
|
||||||
"data": [],
|
|
||||||
"has_more": false,
|
|
||||||
"object": "list",
|
|
||||||
"total_count": 0,
|
|
||||||
"url": "/v1/customers/cus_NORMALIZED0001/tax_ids"
|
|
||||||
},
|
|
||||||
"tax_info": null,
|
"tax_info": null,
|
||||||
"tax_info_verification": null
|
"tax_info_verification": null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
{
|
{
|
||||||
"account_balance": 0,
|
"account_balance": 0,
|
||||||
"address": null,
|
|
||||||
"balance": 0,
|
|
||||||
"created": 1000000000,
|
"created": 1000000000,
|
||||||
"currency": "usd",
|
"currency": "usd",
|
||||||
"default_source": {
|
"default_source": {
|
||||||
@@ -13,17 +11,17 @@
|
|||||||
"address_state": null,
|
"address_state": null,
|
||||||
"address_zip": "33333",
|
"address_zip": "33333",
|
||||||
"address_zip_check": "pass",
|
"address_zip_check": "pass",
|
||||||
"brand": "Visa",
|
"brand": "MasterCard",
|
||||||
"country": "US",
|
"country": "US",
|
||||||
"customer": "cus_NORMALIZED0001",
|
"customer": "cus_NORMALIZED0001",
|
||||||
"cvc_check": "pass",
|
"cvc_check": "pass",
|
||||||
"dynamic_last4": null,
|
"dynamic_last4": null,
|
||||||
"exp_month": 3,
|
"exp_month": 3,
|
||||||
"exp_year": 2033,
|
"exp_year": 2033,
|
||||||
"fingerprint": "NORMALIZED000001",
|
"fingerprint": "NORMALIZED000002",
|
||||||
"funding": "credit",
|
"funding": "credit",
|
||||||
"id": "card_NORMALIZED00000000000001",
|
"id": "card_NORMALIZED00000000000002",
|
||||||
"last4": "4242",
|
"last4": "4444",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"name": "Ada Starr",
|
"name": "Ada Starr",
|
||||||
"object": "card",
|
"object": "card",
|
||||||
@@ -37,7 +35,6 @@
|
|||||||
"invoice_prefix": "NORMA01",
|
"invoice_prefix": "NORMA01",
|
||||||
"invoice_settings": {
|
"invoice_settings": {
|
||||||
"custom_fields": null,
|
"custom_fields": null,
|
||||||
"default_payment_method": null,
|
|
||||||
"footer": null
|
"footer": null
|
||||||
},
|
},
|
||||||
"livemode": false,
|
"livemode": false,
|
||||||
@@ -45,10 +42,7 @@
|
|||||||
"realm_id": "1",
|
"realm_id": "1",
|
||||||
"realm_str": "zulip"
|
"realm_str": "zulip"
|
||||||
},
|
},
|
||||||
"name": null,
|
|
||||||
"object": "customer",
|
"object": "customer",
|
||||||
"phone": null,
|
|
||||||
"preferred_locales": [],
|
|
||||||
"shipping": null,
|
"shipping": null,
|
||||||
"sources": {
|
"sources": {
|
||||||
"data": [
|
"data": [
|
||||||
@@ -61,17 +55,17 @@
|
|||||||
"address_state": null,
|
"address_state": null,
|
||||||
"address_zip": "33333",
|
"address_zip": "33333",
|
||||||
"address_zip_check": "pass",
|
"address_zip_check": "pass",
|
||||||
"brand": "Visa",
|
"brand": "MasterCard",
|
||||||
"country": "US",
|
"country": "US",
|
||||||
"customer": "cus_NORMALIZED0001",
|
"customer": "cus_NORMALIZED0001",
|
||||||
"cvc_check": "pass",
|
"cvc_check": "pass",
|
||||||
"dynamic_last4": null,
|
"dynamic_last4": null,
|
||||||
"exp_month": 3,
|
"exp_month": 3,
|
||||||
"exp_year": 2033,
|
"exp_year": 2033,
|
||||||
"fingerprint": "NORMALIZED000001",
|
"fingerprint": "NORMALIZED000002",
|
||||||
"funding": "credit",
|
"funding": "credit",
|
||||||
"id": "card_NORMALIZED00000000000001",
|
"id": "card_NORMALIZED00000000000002",
|
||||||
"last4": "4242",
|
"last4": "4444",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"name": "Ada Starr",
|
"name": "Ada Starr",
|
||||||
"object": "card",
|
"object": "card",
|
||||||
@@ -90,14 +84,6 @@
|
|||||||
"total_count": 0,
|
"total_count": 0,
|
||||||
"url": "/v1/customers/cus_NORMALIZED0001/subscriptions"
|
"url": "/v1/customers/cus_NORMALIZED0001/subscriptions"
|
||||||
},
|
},
|
||||||
"tax_exempt": "none",
|
|
||||||
"tax_ids": {
|
|
||||||
"data": [],
|
|
||||||
"has_more": false,
|
|
||||||
"object": "list",
|
|
||||||
"total_count": 0,
|
|
||||||
"url": "/v1/customers/cus_NORMALIZED0001/tax_ids"
|
|
||||||
},
|
|
||||||
"tax_info": null,
|
"tax_info": null,
|
||||||
"tax_info_verification": null
|
"tax_info_verification": null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
{
|
{
|
||||||
"account_balance": 0,
|
"account_balance": 0,
|
||||||
"address": null,
|
|
||||||
"balance": 0,
|
|
||||||
"created": 1000000000,
|
"created": 1000000000,
|
||||||
"currency": "usd",
|
"currency": "usd",
|
||||||
"default_source": {
|
"default_source": {
|
||||||
@@ -13,7 +11,7 @@
|
|||||||
"address_state": null,
|
"address_state": null,
|
||||||
"address_zip": "33333",
|
"address_zip": "33333",
|
||||||
"address_zip_check": "pass",
|
"address_zip_check": "pass",
|
||||||
"brand": "Visa",
|
"brand": "MasterCard",
|
||||||
"country": "US",
|
"country": "US",
|
||||||
"customer": "cus_NORMALIZED0001",
|
"customer": "cus_NORMALIZED0001",
|
||||||
"cvc_check": "pass",
|
"cvc_check": "pass",
|
||||||
@@ -23,13 +21,13 @@
|
|||||||
"fingerprint": "NORMALIZED000002",
|
"fingerprint": "NORMALIZED000002",
|
||||||
"funding": "credit",
|
"funding": "credit",
|
||||||
"id": "card_NORMALIZED00000000000002",
|
"id": "card_NORMALIZED00000000000002",
|
||||||
"last4": "0341",
|
"last4": "4444",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"name": "Ada Starr",
|
"name": "Ada Starr",
|
||||||
"object": "card",
|
"object": "card",
|
||||||
"tokenization_method": null
|
"tokenization_method": null
|
||||||
},
|
},
|
||||||
"delinquent": true,
|
"delinquent": false,
|
||||||
"description": "zulip (Zulip Dev)",
|
"description": "zulip (Zulip Dev)",
|
||||||
"discount": null,
|
"discount": null,
|
||||||
"email": "hamlet@zulip.com",
|
"email": "hamlet@zulip.com",
|
||||||
@@ -37,7 +35,6 @@
|
|||||||
"invoice_prefix": "NORMA01",
|
"invoice_prefix": "NORMA01",
|
||||||
"invoice_settings": {
|
"invoice_settings": {
|
||||||
"custom_fields": null,
|
"custom_fields": null,
|
||||||
"default_payment_method": null,
|
|
||||||
"footer": null
|
"footer": null
|
||||||
},
|
},
|
||||||
"livemode": false,
|
"livemode": false,
|
||||||
@@ -45,10 +42,7 @@
|
|||||||
"realm_id": "1",
|
"realm_id": "1",
|
||||||
"realm_str": "zulip"
|
"realm_str": "zulip"
|
||||||
},
|
},
|
||||||
"name": null,
|
|
||||||
"object": "customer",
|
"object": "customer",
|
||||||
"phone": null,
|
|
||||||
"preferred_locales": [],
|
|
||||||
"shipping": null,
|
"shipping": null,
|
||||||
"sources": {
|
"sources": {
|
||||||
"data": [
|
"data": [
|
||||||
@@ -61,7 +55,7 @@
|
|||||||
"address_state": null,
|
"address_state": null,
|
||||||
"address_zip": "33333",
|
"address_zip": "33333",
|
||||||
"address_zip_check": "pass",
|
"address_zip_check": "pass",
|
||||||
"brand": "Visa",
|
"brand": "MasterCard",
|
||||||
"country": "US",
|
"country": "US",
|
||||||
"customer": "cus_NORMALIZED0001",
|
"customer": "cus_NORMALIZED0001",
|
||||||
"cvc_check": "pass",
|
"cvc_check": "pass",
|
||||||
@@ -71,7 +65,7 @@
|
|||||||
"fingerprint": "NORMALIZED000002",
|
"fingerprint": "NORMALIZED000002",
|
||||||
"funding": "credit",
|
"funding": "credit",
|
||||||
"id": "card_NORMALIZED00000000000002",
|
"id": "card_NORMALIZED00000000000002",
|
||||||
"last4": "0341",
|
"last4": "4444",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"name": "Ada Starr",
|
"name": "Ada Starr",
|
||||||
"object": "card",
|
"object": "card",
|
||||||
@@ -90,14 +84,6 @@
|
|||||||
"total_count": 0,
|
"total_count": 0,
|
||||||
"url": "/v1/customers/cus_NORMALIZED0001/subscriptions"
|
"url": "/v1/customers/cus_NORMALIZED0001/subscriptions"
|
||||||
},
|
},
|
||||||
"tax_exempt": "none",
|
|
||||||
"tax_ids": {
|
|
||||||
"data": [],
|
|
||||||
"has_more": false,
|
|
||||||
"object": "list",
|
|
||||||
"total_count": 0,
|
|
||||||
"url": "/v1/customers/cus_NORMALIZED0001/tax_ids"
|
|
||||||
},
|
|
||||||
"tax_info": null,
|
"tax_info": null,
|
||||||
"tax_info_verification": null
|
"tax_info_verification": null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,103 +0,0 @@
|
|||||||
{
|
|
||||||
"account_balance": 0,
|
|
||||||
"address": null,
|
|
||||||
"balance": 0,
|
|
||||||
"created": 1000000000,
|
|
||||||
"currency": "usd",
|
|
||||||
"default_source": {
|
|
||||||
"address_city": "Pacific",
|
|
||||||
"address_country": "United States",
|
|
||||||
"address_line1": "Under the sea,",
|
|
||||||
"address_line1_check": "pass",
|
|
||||||
"address_line2": null,
|
|
||||||
"address_state": null,
|
|
||||||
"address_zip": "33333",
|
|
||||||
"address_zip_check": "pass",
|
|
||||||
"brand": "Visa",
|
|
||||||
"country": "US",
|
|
||||||
"customer": "cus_NORMALIZED0001",
|
|
||||||
"cvc_check": "pass",
|
|
||||||
"dynamic_last4": null,
|
|
||||||
"exp_month": 3,
|
|
||||||
"exp_year": 2033,
|
|
||||||
"fingerprint": "NORMALIZED000002",
|
|
||||||
"funding": "credit",
|
|
||||||
"id": "card_NORMALIZED00000000000002",
|
|
||||||
"last4": "0341",
|
|
||||||
"metadata": {},
|
|
||||||
"name": "Ada Starr",
|
|
||||||
"object": "card",
|
|
||||||
"tokenization_method": null
|
|
||||||
},
|
|
||||||
"delinquent": true,
|
|
||||||
"description": "zulip (Zulip Dev)",
|
|
||||||
"discount": null,
|
|
||||||
"email": "hamlet@zulip.com",
|
|
||||||
"id": "cus_NORMALIZED0001",
|
|
||||||
"invoice_prefix": "NORMA01",
|
|
||||||
"invoice_settings": {
|
|
||||||
"custom_fields": null,
|
|
||||||
"default_payment_method": null,
|
|
||||||
"footer": null
|
|
||||||
},
|
|
||||||
"livemode": false,
|
|
||||||
"metadata": {
|
|
||||||
"realm_id": "1",
|
|
||||||
"realm_str": "zulip"
|
|
||||||
},
|
|
||||||
"name": null,
|
|
||||||
"object": "customer",
|
|
||||||
"phone": null,
|
|
||||||
"preferred_locales": [],
|
|
||||||
"shipping": null,
|
|
||||||
"sources": {
|
|
||||||
"data": [
|
|
||||||
{
|
|
||||||
"address_city": "Pacific",
|
|
||||||
"address_country": "United States",
|
|
||||||
"address_line1": "Under the sea,",
|
|
||||||
"address_line1_check": "pass",
|
|
||||||
"address_line2": null,
|
|
||||||
"address_state": null,
|
|
||||||
"address_zip": "33333",
|
|
||||||
"address_zip_check": "pass",
|
|
||||||
"brand": "Visa",
|
|
||||||
"country": "US",
|
|
||||||
"customer": "cus_NORMALIZED0001",
|
|
||||||
"cvc_check": "pass",
|
|
||||||
"dynamic_last4": null,
|
|
||||||
"exp_month": 3,
|
|
||||||
"exp_year": 2033,
|
|
||||||
"fingerprint": "NORMALIZED000002",
|
|
||||||
"funding": "credit",
|
|
||||||
"id": "card_NORMALIZED00000000000002",
|
|
||||||
"last4": "0341",
|
|
||||||
"metadata": {},
|
|
||||||
"name": "Ada Starr",
|
|
||||||
"object": "card",
|
|
||||||
"tokenization_method": null
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"has_more": false,
|
|
||||||
"object": "list",
|
|
||||||
"total_count": 1,
|
|
||||||
"url": "/v1/customers/cus_NORMALIZED0001/sources"
|
|
||||||
},
|
|
||||||
"subscriptions": {
|
|
||||||
"data": [],
|
|
||||||
"has_more": false,
|
|
||||||
"object": "list",
|
|
||||||
"total_count": 0,
|
|
||||||
"url": "/v1/customers/cus_NORMALIZED0001/subscriptions"
|
|
||||||
},
|
|
||||||
"tax_exempt": "none",
|
|
||||||
"tax_ids": {
|
|
||||||
"data": [],
|
|
||||||
"has_more": false,
|
|
||||||
"object": "list",
|
|
||||||
"total_count": 0,
|
|
||||||
"url": "/v1/customers/cus_NORMALIZED0001/tax_ids"
|
|
||||||
},
|
|
||||||
"tax_info": null,
|
|
||||||
"tax_info_verification": null
|
|
||||||
}
|
|
||||||
@@ -1,103 +0,0 @@
|
|||||||
{
|
|
||||||
"account_balance": 0,
|
|
||||||
"address": null,
|
|
||||||
"balance": 0,
|
|
||||||
"created": 1000000000,
|
|
||||||
"currency": "usd",
|
|
||||||
"default_source": {
|
|
||||||
"address_city": "Pacific",
|
|
||||||
"address_country": "United States",
|
|
||||||
"address_line1": "Under the sea,",
|
|
||||||
"address_line1_check": "pass",
|
|
||||||
"address_line2": null,
|
|
||||||
"address_state": null,
|
|
||||||
"address_zip": "33333",
|
|
||||||
"address_zip_check": "pass",
|
|
||||||
"brand": "MasterCard",
|
|
||||||
"country": "US",
|
|
||||||
"customer": "cus_NORMALIZED0001",
|
|
||||||
"cvc_check": "pass",
|
|
||||||
"dynamic_last4": null,
|
|
||||||
"exp_month": 3,
|
|
||||||
"exp_year": 2033,
|
|
||||||
"fingerprint": "NORMALIZED000003",
|
|
||||||
"funding": "credit",
|
|
||||||
"id": "card_NORMALIZED00000000000003",
|
|
||||||
"last4": "4444",
|
|
||||||
"metadata": {},
|
|
||||||
"name": "Ada Starr",
|
|
||||||
"object": "card",
|
|
||||||
"tokenization_method": null
|
|
||||||
},
|
|
||||||
"delinquent": false,
|
|
||||||
"description": "zulip (Zulip Dev)",
|
|
||||||
"discount": null,
|
|
||||||
"email": "hamlet@zulip.com",
|
|
||||||
"id": "cus_NORMALIZED0001",
|
|
||||||
"invoice_prefix": "NORMA01",
|
|
||||||
"invoice_settings": {
|
|
||||||
"custom_fields": null,
|
|
||||||
"default_payment_method": null,
|
|
||||||
"footer": null
|
|
||||||
},
|
|
||||||
"livemode": false,
|
|
||||||
"metadata": {
|
|
||||||
"realm_id": "1",
|
|
||||||
"realm_str": "zulip"
|
|
||||||
},
|
|
||||||
"name": null,
|
|
||||||
"object": "customer",
|
|
||||||
"phone": null,
|
|
||||||
"preferred_locales": [],
|
|
||||||
"shipping": null,
|
|
||||||
"sources": {
|
|
||||||
"data": [
|
|
||||||
{
|
|
||||||
"address_city": "Pacific",
|
|
||||||
"address_country": "United States",
|
|
||||||
"address_line1": "Under the sea,",
|
|
||||||
"address_line1_check": "pass",
|
|
||||||
"address_line2": null,
|
|
||||||
"address_state": null,
|
|
||||||
"address_zip": "33333",
|
|
||||||
"address_zip_check": "pass",
|
|
||||||
"brand": "MasterCard",
|
|
||||||
"country": "US",
|
|
||||||
"customer": "cus_NORMALIZED0001",
|
|
||||||
"cvc_check": "pass",
|
|
||||||
"dynamic_last4": null,
|
|
||||||
"exp_month": 3,
|
|
||||||
"exp_year": 2033,
|
|
||||||
"fingerprint": "NORMALIZED000003",
|
|
||||||
"funding": "credit",
|
|
||||||
"id": "card_NORMALIZED00000000000003",
|
|
||||||
"last4": "4444",
|
|
||||||
"metadata": {},
|
|
||||||
"name": "Ada Starr",
|
|
||||||
"object": "card",
|
|
||||||
"tokenization_method": null
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"has_more": false,
|
|
||||||
"object": "list",
|
|
||||||
"total_count": 1,
|
|
||||||
"url": "/v1/customers/cus_NORMALIZED0001/sources"
|
|
||||||
},
|
|
||||||
"subscriptions": {
|
|
||||||
"data": [],
|
|
||||||
"has_more": false,
|
|
||||||
"object": "list",
|
|
||||||
"total_count": 0,
|
|
||||||
"url": "/v1/customers/cus_NORMALIZED0001/subscriptions"
|
|
||||||
},
|
|
||||||
"tax_exempt": "none",
|
|
||||||
"tax_ids": {
|
|
||||||
"data": [],
|
|
||||||
"has_more": false,
|
|
||||||
"object": "list",
|
|
||||||
"total_count": 0,
|
|
||||||
"url": "/v1/customers/cus_NORMALIZED0001/tax_ids"
|
|
||||||
},
|
|
||||||
"tax_info": null,
|
|
||||||
"tax_info_verification": null
|
|
||||||
}
|
|
||||||
@@ -1,36 +1,65 @@
|
|||||||
{
|
{
|
||||||
"_message": "Your card was declined.",
|
"account_balance": 0,
|
||||||
"code": "card_declined",
|
"created": 1000000000,
|
||||||
"headers": {
|
"currency": "usd",
|
||||||
"Connection": "keep-alive",
|
"default_source": "card_NORMALIZED00000000000002",
|
||||||
"Content-Length": "235",
|
"delinquent": false,
|
||||||
"Content-Type": "application/json",
|
"description": "zulip (Zulip Dev)",
|
||||||
"Date": "NORMALIZED DATETIME",
|
"discount": null,
|
||||||
"Server": "nginx",
|
"email": "hamlet@zulip.com",
|
||||||
"Strict-Transport-Security": "max-age=31556926; includeSubDomains; preload",
|
"id": "cus_NORMALIZED0001",
|
||||||
"access-control-allow-credentials": "true",
|
"invoice_prefix": "NORMA01",
|
||||||
"access-control-allow-methods": "GET, POST, HEAD, OPTIONS, DELETE",
|
"invoice_settings": {
|
||||||
"access-control-allow-origin": "*",
|
"custom_fields": null,
|
||||||
"access-control-expose-headers": "Request-Id, Stripe-Manage-Version, X-Stripe-External-Auth-Required, X-Stripe-Privileged-Session-Required",
|
"footer": null
|
||||||
"access-control-max-age": "300",
|
|
||||||
"cache-control": "no-cache, no-store",
|
|
||||||
"idempotency-key": "00000000-0000-0000-0000-000000000000",
|
|
||||||
"original-request": "req_NORMALIZED0001",
|
|
||||||
"request-id": "req_NORMALIZED0001",
|
|
||||||
"stripe-version": "2019-03-14"
|
|
||||||
},
|
},
|
||||||
"http_body": "{\n \"error\": {\n \"code\": \"card_declined\",\n \"decline_code\": \"lost_card\",\n \"doc_url\": \"https://stripe.com/docs/error-codes/card-declined\",\n \"message\": \"Your card was declined.\",\n \"param\": \"\",\n \"type\": \"card_error\"\n }\n}\n",
|
"livemode": false,
|
||||||
"http_status": 402,
|
"metadata": {
|
||||||
"json_body": {
|
"realm_id": "1",
|
||||||
"error": {
|
"realm_str": "zulip"
|
||||||
"code": "card_declined",
|
|
||||||
"decline_code": "lost_card",
|
|
||||||
"doc_url": "https://stripe.com/docs/error-codes/card-declined",
|
|
||||||
"message": "Your card was declined.",
|
|
||||||
"param": "",
|
|
||||||
"type": "card_error"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"param": "",
|
"object": "customer",
|
||||||
"request_id": "req_NORMALIZED0001"
|
"shipping": null,
|
||||||
|
"sources": {
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"address_city": "Pacific",
|
||||||
|
"address_country": "United States",
|
||||||
|
"address_line1": "Under the sea,",
|
||||||
|
"address_line1_check": "pass",
|
||||||
|
"address_line2": null,
|
||||||
|
"address_state": null,
|
||||||
|
"address_zip": "33333",
|
||||||
|
"address_zip_check": "pass",
|
||||||
|
"brand": "MasterCard",
|
||||||
|
"country": "US",
|
||||||
|
"customer": "cus_NORMALIZED0001",
|
||||||
|
"cvc_check": "pass",
|
||||||
|
"dynamic_last4": null,
|
||||||
|
"exp_month": 3,
|
||||||
|
"exp_year": 2033,
|
||||||
|
"fingerprint": "NORMALIZED000002",
|
||||||
|
"funding": "credit",
|
||||||
|
"id": "card_NORMALIZED00000000000002",
|
||||||
|
"last4": "4444",
|
||||||
|
"metadata": {},
|
||||||
|
"name": "Ada Starr",
|
||||||
|
"object": "card",
|
||||||
|
"tokenization_method": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"has_more": false,
|
||||||
|
"object": "list",
|
||||||
|
"total_count": 1,
|
||||||
|
"url": "/v1/customers/cus_NORMALIZED0001/sources"
|
||||||
|
},
|
||||||
|
"subscriptions": {
|
||||||
|
"data": [],
|
||||||
|
"has_more": false,
|
||||||
|
"object": "list",
|
||||||
|
"total_count": 0,
|
||||||
|
"url": "/v1/customers/cus_NORMALIZED0001/subscriptions"
|
||||||
|
},
|
||||||
|
"tax_info": null,
|
||||||
|
"tax_info_verification": null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,79 +1,36 @@
|
|||||||
{
|
{
|
||||||
"account_balance": 0,
|
"_message": "Your card was declined.",
|
||||||
"address": null,
|
"code": "card_declined",
|
||||||
"balance": 0,
|
"headers": {
|
||||||
"created": 1000000000,
|
"Access-Control-Allow-Credentials": "true",
|
||||||
"currency": "usd",
|
"Access-Control-Allow-Methods": "GET, POST, HEAD, OPTIONS, DELETE",
|
||||||
"default_source": "card_NORMALIZED00000000000002",
|
"Access-Control-Allow-Origin": "*",
|
||||||
"delinquent": false,
|
"Access-Control-Expose-Headers": "Request-Id, Stripe-Manage-Version, X-Stripe-External-Auth-Required, X-Stripe-Privileged-Session-Required",
|
||||||
"description": "zulip (Zulip Dev)",
|
"Access-Control-Max-Age": "300",
|
||||||
"discount": null,
|
"Cache-Control": "no-cache, no-store",
|
||||||
"email": "hamlet@zulip.com",
|
"Connection": "keep-alive",
|
||||||
"id": "cus_NORMALIZED0001",
|
"Content-Length": "235",
|
||||||
"invoice_prefix": "NORMA01",
|
"Content-Type": "application/json",
|
||||||
"invoice_settings": {
|
"Date": "NORMALIZED DATETIME",
|
||||||
"custom_fields": null,
|
"Idempotency-Key": "7b90998a-36d2-459e-9fd7-8f6e442bda0e",
|
||||||
"default_payment_method": null,
|
"Original-Request": "req_NORMALIZED0001",
|
||||||
"footer": null
|
"Request-Id": "req_NORMALIZED0001",
|
||||||
|
"Server": "nginx",
|
||||||
|
"Strict-Transport-Security": "max-age=31556926; includeSubDomains; preload",
|
||||||
|
"Stripe-Version": "2018-11-08"
|
||||||
},
|
},
|
||||||
"livemode": false,
|
"http_body": "{\n \"error\": {\n \"code\": \"card_declined\",\n \"decline_code\": \"lost_card\",\n \"doc_url\": \"https://stripe.com/docs/error-codes/card-declined\",\n \"message\": \"Your card was declined.\",\n \"param\": \"\",\n \"type\": \"card_error\"\n }\n}\n",
|
||||||
"metadata": {
|
"http_status": 402,
|
||||||
"realm_id": "1",
|
"json_body": {
|
||||||
"realm_str": "zulip"
|
"error": {
|
||||||
|
"code": "card_declined",
|
||||||
|
"decline_code": "lost_card",
|
||||||
|
"doc_url": "https://stripe.com/docs/error-codes/card-declined",
|
||||||
|
"message": "Your card was declined.",
|
||||||
|
"param": "",
|
||||||
|
"type": "card_error"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"name": null,
|
"param": "",
|
||||||
"object": "customer",
|
"request_id": "req_NORMALIZED0001"
|
||||||
"phone": null,
|
|
||||||
"preferred_locales": [],
|
|
||||||
"shipping": null,
|
|
||||||
"sources": {
|
|
||||||
"data": [
|
|
||||||
{
|
|
||||||
"address_city": "Pacific",
|
|
||||||
"address_country": "United States",
|
|
||||||
"address_line1": "Under the sea,",
|
|
||||||
"address_line1_check": "pass",
|
|
||||||
"address_line2": null,
|
|
||||||
"address_state": null,
|
|
||||||
"address_zip": "33333",
|
|
||||||
"address_zip_check": "pass",
|
|
||||||
"brand": "Visa",
|
|
||||||
"country": "US",
|
|
||||||
"customer": "cus_NORMALIZED0001",
|
|
||||||
"cvc_check": "pass",
|
|
||||||
"dynamic_last4": null,
|
|
||||||
"exp_month": 3,
|
|
||||||
"exp_year": 2033,
|
|
||||||
"fingerprint": "NORMALIZED000002",
|
|
||||||
"funding": "credit",
|
|
||||||
"id": "card_NORMALIZED00000000000002",
|
|
||||||
"last4": "0341",
|
|
||||||
"metadata": {},
|
|
||||||
"name": "Ada Starr",
|
|
||||||
"object": "card",
|
|
||||||
"tokenization_method": null
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"has_more": false,
|
|
||||||
"object": "list",
|
|
||||||
"total_count": 1,
|
|
||||||
"url": "/v1/customers/cus_NORMALIZED0001/sources"
|
|
||||||
},
|
|
||||||
"subscriptions": {
|
|
||||||
"data": [],
|
|
||||||
"has_more": false,
|
|
||||||
"object": "list",
|
|
||||||
"total_count": 0,
|
|
||||||
"url": "/v1/customers/cus_NORMALIZED0001/subscriptions"
|
|
||||||
},
|
|
||||||
"tax_exempt": "none",
|
|
||||||
"tax_ids": {
|
|
||||||
"data": [],
|
|
||||||
"has_more": false,
|
|
||||||
"object": "list",
|
|
||||||
"total_count": 0,
|
|
||||||
"url": "/v1/customers/cus_NORMALIZED0001/tax_ids"
|
|
||||||
},
|
|
||||||
"tax_info": null,
|
|
||||||
"tax_info_verification": null
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,79 +0,0 @@
|
|||||||
{
|
|
||||||
"account_balance": 0,
|
|
||||||
"address": null,
|
|
||||||
"balance": 0,
|
|
||||||
"created": 1000000000,
|
|
||||||
"currency": "usd",
|
|
||||||
"default_source": "card_NORMALIZED00000000000003",
|
|
||||||
"delinquent": true,
|
|
||||||
"description": "zulip (Zulip Dev)",
|
|
||||||
"discount": null,
|
|
||||||
"email": "hamlet@zulip.com",
|
|
||||||
"id": "cus_NORMALIZED0001",
|
|
||||||
"invoice_prefix": "NORMA01",
|
|
||||||
"invoice_settings": {
|
|
||||||
"custom_fields": null,
|
|
||||||
"default_payment_method": null,
|
|
||||||
"footer": null
|
|
||||||
},
|
|
||||||
"livemode": false,
|
|
||||||
"metadata": {
|
|
||||||
"realm_id": "1",
|
|
||||||
"realm_str": "zulip"
|
|
||||||
},
|
|
||||||
"name": null,
|
|
||||||
"object": "customer",
|
|
||||||
"phone": null,
|
|
||||||
"preferred_locales": [],
|
|
||||||
"shipping": null,
|
|
||||||
"sources": {
|
|
||||||
"data": [
|
|
||||||
{
|
|
||||||
"address_city": "Pacific",
|
|
||||||
"address_country": "United States",
|
|
||||||
"address_line1": "Under the sea,",
|
|
||||||
"address_line1_check": "pass",
|
|
||||||
"address_line2": null,
|
|
||||||
"address_state": null,
|
|
||||||
"address_zip": "33333",
|
|
||||||
"address_zip_check": "pass",
|
|
||||||
"brand": "MasterCard",
|
|
||||||
"country": "US",
|
|
||||||
"customer": "cus_NORMALIZED0001",
|
|
||||||
"cvc_check": "pass",
|
|
||||||
"dynamic_last4": null,
|
|
||||||
"exp_month": 3,
|
|
||||||
"exp_year": 2033,
|
|
||||||
"fingerprint": "NORMALIZED000003",
|
|
||||||
"funding": "credit",
|
|
||||||
"id": "card_NORMALIZED00000000000003",
|
|
||||||
"last4": "4444",
|
|
||||||
"metadata": {},
|
|
||||||
"name": "Ada Starr",
|
|
||||||
"object": "card",
|
|
||||||
"tokenization_method": null
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"has_more": false,
|
|
||||||
"object": "list",
|
|
||||||
"total_count": 1,
|
|
||||||
"url": "/v1/customers/cus_NORMALIZED0001/sources"
|
|
||||||
},
|
|
||||||
"subscriptions": {
|
|
||||||
"data": [],
|
|
||||||
"has_more": false,
|
|
||||||
"object": "list",
|
|
||||||
"total_count": 0,
|
|
||||||
"url": "/v1/customers/cus_NORMALIZED0001/subscriptions"
|
|
||||||
},
|
|
||||||
"tax_exempt": "none",
|
|
||||||
"tax_ids": {
|
|
||||||
"data": [],
|
|
||||||
"has_more": false,
|
|
||||||
"object": "list",
|
|
||||||
"total_count": 0,
|
|
||||||
"url": "/v1/customers/cus_NORMALIZED0001/tax_ids"
|
|
||||||
},
|
|
||||||
"tax_info": null,
|
|
||||||
"tax_info_verification": null
|
|
||||||
}
|
|
||||||
@@ -1,35 +1,24 @@
|
|||||||
{
|
{
|
||||||
"account_country": "US",
|
|
||||||
"account_name": "Dev account",
|
|
||||||
"amount_due": 0,
|
"amount_due": 0,
|
||||||
"amount_paid": 0,
|
"amount_paid": 0,
|
||||||
"amount_remaining": 0,
|
"amount_remaining": 0,
|
||||||
"application_fee_amount": null,
|
"application_fee": null,
|
||||||
"attempt_count": 0,
|
"attempt_count": 0,
|
||||||
"attempted": false,
|
"attempted": false,
|
||||||
"auto_advance": true,
|
"auto_advance": true,
|
||||||
"billing": "charge_automatically",
|
"billing": "charge_automatically",
|
||||||
"billing_reason": "manual",
|
"billing_reason": "manual",
|
||||||
"charge": null,
|
"charge": null,
|
||||||
"collection_method": "charge_automatically",
|
|
||||||
"created": 1000000000,
|
|
||||||
"currency": "usd",
|
"currency": "usd",
|
||||||
"custom_fields": null,
|
"custom_fields": null,
|
||||||
"customer": "cus_NORMALIZED0001",
|
"customer": "cus_NORMALIZED0001",
|
||||||
"customer_address": null,
|
"date": 1000000000,
|
||||||
"customer_email": "hamlet@zulip.com",
|
|
||||||
"customer_name": null,
|
|
||||||
"customer_phone": null,
|
|
||||||
"customer_shipping": null,
|
|
||||||
"customer_tax_exempt": "none",
|
|
||||||
"customer_tax_ids": [],
|
|
||||||
"default_payment_method": null,
|
|
||||||
"default_source": null,
|
"default_source": null,
|
||||||
"default_tax_rates": [],
|
|
||||||
"description": "",
|
"description": "",
|
||||||
"discount": null,
|
"discount": null,
|
||||||
"due_date": null,
|
"due_date": null,
|
||||||
"ending_balance": null,
|
"ending_balance": null,
|
||||||
|
"finalized_at": null,
|
||||||
"footer": null,
|
"footer": null,
|
||||||
"hosted_invoice_url": null,
|
"hosted_invoice_url": null,
|
||||||
"id": "in_NORMALIZED00000000000001",
|
"id": "in_NORMALIZED00000000000001",
|
||||||
@@ -54,8 +43,6 @@
|
|||||||
"proration": false,
|
"proration": false,
|
||||||
"quantity": 6,
|
"quantity": 6,
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"tax_amounts": [],
|
|
||||||
"tax_rates": [],
|
|
||||||
"type": "invoiceitem"
|
"type": "invoiceitem"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -76,8 +63,6 @@
|
|||||||
"proration": false,
|
"proration": false,
|
||||||
"quantity": 1,
|
"quantity": 1,
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"tax_amounts": [],
|
|
||||||
"tax_rates": [],
|
|
||||||
"type": "invoiceitem"
|
"type": "invoiceitem"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -92,26 +77,16 @@
|
|||||||
"number": "NORMALI-0001",
|
"number": "NORMALI-0001",
|
||||||
"object": "invoice",
|
"object": "invoice",
|
||||||
"paid": false,
|
"paid": false,
|
||||||
"payment_intent": null,
|
|
||||||
"period_end": 1000000000,
|
"period_end": 1000000000,
|
||||||
"period_start": 1000000000,
|
"period_start": 1000000000,
|
||||||
"post_payment_credit_notes_amount": 0,
|
|
||||||
"pre_payment_credit_notes_amount": 0,
|
|
||||||
"receipt_number": null,
|
"receipt_number": null,
|
||||||
"starting_balance": 0,
|
"starting_balance": 0,
|
||||||
"statement_descriptor": "Zulip Standard",
|
"statement_descriptor": "Zulip Standard",
|
||||||
"status": "draft",
|
"status": "draft",
|
||||||
"status_transitions": {
|
|
||||||
"finalized_at": null,
|
|
||||||
"marked_uncollectible_at": null,
|
|
||||||
"paid_at": null,
|
|
||||||
"voided_at": null
|
|
||||||
},
|
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"subtotal": 0,
|
"subtotal": 0,
|
||||||
"tax": null,
|
"tax": 0,
|
||||||
"tax_percent": null,
|
"tax_percent": null,
|
||||||
"total": 0,
|
"total": 0,
|
||||||
"total_tax_amounts": [],
|
|
||||||
"webhooks_delivered_at": null
|
"webhooks_delivered_at": null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,95 +0,0 @@
|
|||||||
{
|
|
||||||
"account_country": "US",
|
|
||||||
"account_name": "Dev account",
|
|
||||||
"amount_due": 5000,
|
|
||||||
"amount_paid": 0,
|
|
||||||
"amount_remaining": 5000,
|
|
||||||
"application_fee_amount": null,
|
|
||||||
"attempt_count": 0,
|
|
||||||
"attempted": false,
|
|
||||||
"auto_advance": false,
|
|
||||||
"billing": "charge_automatically",
|
|
||||||
"billing_reason": "manual",
|
|
||||||
"charge": null,
|
|
||||||
"collection_method": "charge_automatically",
|
|
||||||
"created": 1000000000,
|
|
||||||
"currency": "usd",
|
|
||||||
"custom_fields": null,
|
|
||||||
"customer": "cus_NORMALIZED0001",
|
|
||||||
"customer_address": null,
|
|
||||||
"customer_email": "hamlet@zulip.com",
|
|
||||||
"customer_name": null,
|
|
||||||
"customer_phone": null,
|
|
||||||
"customer_shipping": null,
|
|
||||||
"customer_tax_exempt": "none",
|
|
||||||
"customer_tax_ids": [],
|
|
||||||
"default_payment_method": null,
|
|
||||||
"default_source": null,
|
|
||||||
"default_tax_rates": [],
|
|
||||||
"description": "",
|
|
||||||
"discount": null,
|
|
||||||
"due_date": null,
|
|
||||||
"ending_balance": null,
|
|
||||||
"footer": null,
|
|
||||||
"hosted_invoice_url": null,
|
|
||||||
"id": "in_NORMALIZED00000000000002",
|
|
||||||
"invoice_pdf": null,
|
|
||||||
"lines": {
|
|
||||||
"data": [
|
|
||||||
{
|
|
||||||
"amount": 5000,
|
|
||||||
"currency": "usd",
|
|
||||||
"description": null,
|
|
||||||
"discountable": true,
|
|
||||||
"id": "ii_NORMALIZED00000000000003",
|
|
||||||
"invoice_item": "ii_NORMALIZED00000000000003",
|
|
||||||
"livemode": false,
|
|
||||||
"metadata": {},
|
|
||||||
"object": "line_item",
|
|
||||||
"period": {
|
|
||||||
"end": 1000000000,
|
|
||||||
"start": 1000000000
|
|
||||||
},
|
|
||||||
"plan": null,
|
|
||||||
"proration": false,
|
|
||||||
"quantity": 1,
|
|
||||||
"subscription": null,
|
|
||||||
"tax_amounts": [],
|
|
||||||
"tax_rates": [],
|
|
||||||
"type": "invoiceitem"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"has_more": false,
|
|
||||||
"object": "list",
|
|
||||||
"total_count": 1,
|
|
||||||
"url": "/v1/invoices/in_NORMALIZED00000000000002/lines"
|
|
||||||
},
|
|
||||||
"livemode": false,
|
|
||||||
"metadata": {},
|
|
||||||
"next_payment_attempt": null,
|
|
||||||
"number": "NORMALI-0002",
|
|
||||||
"object": "invoice",
|
|
||||||
"paid": false,
|
|
||||||
"payment_intent": null,
|
|
||||||
"period_end": 1000000000,
|
|
||||||
"period_start": 1000000000,
|
|
||||||
"post_payment_credit_notes_amount": 0,
|
|
||||||
"pre_payment_credit_notes_amount": 0,
|
|
||||||
"receipt_number": null,
|
|
||||||
"starting_balance": 0,
|
|
||||||
"statement_descriptor": null,
|
|
||||||
"status": "draft",
|
|
||||||
"status_transitions": {
|
|
||||||
"finalized_at": null,
|
|
||||||
"marked_uncollectible_at": null,
|
|
||||||
"paid_at": null,
|
|
||||||
"voided_at": null
|
|
||||||
},
|
|
||||||
"subscription": null,
|
|
||||||
"subtotal": 5000,
|
|
||||||
"tax": null,
|
|
||||||
"tax_percent": null,
|
|
||||||
"total": 5000,
|
|
||||||
"total_tax_amounts": [],
|
|
||||||
"webhooks_delivered_at": null
|
|
||||||
}
|
|
||||||
@@ -1,35 +1,24 @@
|
|||||||
{
|
{
|
||||||
"account_country": "US",
|
|
||||||
"account_name": "Dev account",
|
|
||||||
"amount_due": 0,
|
"amount_due": 0,
|
||||||
"amount_paid": 0,
|
"amount_paid": 0,
|
||||||
"amount_remaining": 0,
|
"amount_remaining": 0,
|
||||||
"application_fee_amount": null,
|
"application_fee": null,
|
||||||
"attempt_count": 0,
|
"attempt_count": 0,
|
||||||
"attempted": true,
|
"attempted": true,
|
||||||
"auto_advance": false,
|
"auto_advance": false,
|
||||||
"billing": "charge_automatically",
|
"billing": "charge_automatically",
|
||||||
"billing_reason": "manual",
|
"billing_reason": "manual",
|
||||||
"charge": null,
|
"charge": null,
|
||||||
"collection_method": "charge_automatically",
|
|
||||||
"created": 1000000000,
|
|
||||||
"currency": "usd",
|
"currency": "usd",
|
||||||
"custom_fields": null,
|
"custom_fields": null,
|
||||||
"customer": "cus_NORMALIZED0001",
|
"customer": "cus_NORMALIZED0001",
|
||||||
"customer_address": null,
|
"date": 1000000000,
|
||||||
"customer_email": "hamlet@zulip.com",
|
|
||||||
"customer_name": null,
|
|
||||||
"customer_phone": null,
|
|
||||||
"customer_shipping": null,
|
|
||||||
"customer_tax_exempt": "none",
|
|
||||||
"customer_tax_ids": [],
|
|
||||||
"default_payment_method": null,
|
|
||||||
"default_source": null,
|
"default_source": null,
|
||||||
"default_tax_rates": [],
|
|
||||||
"description": "",
|
"description": "",
|
||||||
"discount": null,
|
"discount": null,
|
||||||
"due_date": 1000000000,
|
"due_date": 1000000000,
|
||||||
"ending_balance": 0,
|
"ending_balance": 0,
|
||||||
|
"finalized_at": 1000000000,
|
||||||
"footer": null,
|
"footer": null,
|
||||||
"hosted_invoice_url": "https://pay.stripe.com/invoice/invst_NORMALIZED0000000000000001",
|
"hosted_invoice_url": "https://pay.stripe.com/invoice/invst_NORMALIZED0000000000000001",
|
||||||
"id": "in_NORMALIZED00000000000001",
|
"id": "in_NORMALIZED00000000000001",
|
||||||
@@ -54,8 +43,6 @@
|
|||||||
"proration": false,
|
"proration": false,
|
||||||
"quantity": 6,
|
"quantity": 6,
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"tax_amounts": [],
|
|
||||||
"tax_rates": [],
|
|
||||||
"type": "invoiceitem"
|
"type": "invoiceitem"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -76,8 +63,6 @@
|
|||||||
"proration": false,
|
"proration": false,
|
||||||
"quantity": 1,
|
"quantity": 1,
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"tax_amounts": [],
|
|
||||||
"tax_rates": [],
|
|
||||||
"type": "invoiceitem"
|
"type": "invoiceitem"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -92,26 +77,16 @@
|
|||||||
"number": "NORMALI-0001",
|
"number": "NORMALI-0001",
|
||||||
"object": "invoice",
|
"object": "invoice",
|
||||||
"paid": true,
|
"paid": true,
|
||||||
"payment_intent": null,
|
|
||||||
"period_end": 1000000000,
|
"period_end": 1000000000,
|
||||||
"period_start": 1000000000,
|
"period_start": 1000000000,
|
||||||
"post_payment_credit_notes_amount": 0,
|
|
||||||
"pre_payment_credit_notes_amount": 0,
|
|
||||||
"receipt_number": null,
|
"receipt_number": null,
|
||||||
"starting_balance": 0,
|
"starting_balance": 0,
|
||||||
"statement_descriptor": "Zulip Standard",
|
"statement_descriptor": "Zulip Standard",
|
||||||
"status": "paid",
|
"status": "paid",
|
||||||
"status_transitions": {
|
|
||||||
"finalized_at": 1000000000,
|
|
||||||
"marked_uncollectible_at": null,
|
|
||||||
"paid_at": 1000000000,
|
|
||||||
"voided_at": null
|
|
||||||
},
|
|
||||||
"subscription": null,
|
"subscription": null,
|
||||||
"subtotal": 0,
|
"subtotal": 0,
|
||||||
"tax": null,
|
"tax": 0,
|
||||||
"tax_percent": null,
|
"tax_percent": null,
|
||||||
"total": 0,
|
"total": 0,
|
||||||
"total_tax_amounts": [],
|
|
||||||
"webhooks_delivered_at": 1000000000
|
"webhooks_delivered_at": 1000000000
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,95 +0,0 @@
|
|||||||
{
|
|
||||||
"account_country": "US",
|
|
||||||
"account_name": "Dev account",
|
|
||||||
"amount_due": 5000,
|
|
||||||
"amount_paid": 0,
|
|
||||||
"amount_remaining": 5000,
|
|
||||||
"application_fee_amount": null,
|
|
||||||
"attempt_count": 0,
|
|
||||||
"attempted": false,
|
|
||||||
"auto_advance": false,
|
|
||||||
"billing": "charge_automatically",
|
|
||||||
"billing_reason": "manual",
|
|
||||||
"charge": null,
|
|
||||||
"collection_method": "charge_automatically",
|
|
||||||
"created": 1000000000,
|
|
||||||
"currency": "usd",
|
|
||||||
"custom_fields": null,
|
|
||||||
"customer": "cus_NORMALIZED0001",
|
|
||||||
"customer_address": null,
|
|
||||||
"customer_email": "hamlet@zulip.com",
|
|
||||||
"customer_name": null,
|
|
||||||
"customer_phone": null,
|
|
||||||
"customer_shipping": null,
|
|
||||||
"customer_tax_exempt": "none",
|
|
||||||
"customer_tax_ids": [],
|
|
||||||
"default_payment_method": null,
|
|
||||||
"default_source": null,
|
|
||||||
"default_tax_rates": [],
|
|
||||||
"description": "",
|
|
||||||
"discount": null,
|
|
||||||
"due_date": 1000000000,
|
|
||||||
"ending_balance": 0,
|
|
||||||
"footer": null,
|
|
||||||
"hosted_invoice_url": "https://pay.stripe.com/invoice/invst_NORMALIZED0000000000000002",
|
|
||||||
"id": "in_NORMALIZED00000000000002",
|
|
||||||
"invoice_pdf": "https://pay.stripe.com/invoice/invst_NORMALIZED0000000000000002/pdf",
|
|
||||||
"lines": {
|
|
||||||
"data": [
|
|
||||||
{
|
|
||||||
"amount": 5000,
|
|
||||||
"currency": "usd",
|
|
||||||
"description": null,
|
|
||||||
"discountable": true,
|
|
||||||
"id": "ii_NORMALIZED00000000000003",
|
|
||||||
"invoice_item": "ii_NORMALIZED00000000000003",
|
|
||||||
"livemode": false,
|
|
||||||
"metadata": {},
|
|
||||||
"object": "line_item",
|
|
||||||
"period": {
|
|
||||||
"end": 1000000000,
|
|
||||||
"start": 1000000000
|
|
||||||
},
|
|
||||||
"plan": null,
|
|
||||||
"proration": false,
|
|
||||||
"quantity": 1,
|
|
||||||
"subscription": null,
|
|
||||||
"tax_amounts": [],
|
|
||||||
"tax_rates": [],
|
|
||||||
"type": "invoiceitem"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"has_more": false,
|
|
||||||
"object": "list",
|
|
||||||
"total_count": 1,
|
|
||||||
"url": "/v1/invoices/in_NORMALIZED00000000000002/lines"
|
|
||||||
},
|
|
||||||
"livemode": false,
|
|
||||||
"metadata": {},
|
|
||||||
"next_payment_attempt": null,
|
|
||||||
"number": "NORMALI-0002",
|
|
||||||
"object": "invoice",
|
|
||||||
"paid": false,
|
|
||||||
"payment_intent": "pi_1F96BhGh0CmXqmnwXdcsnxHY",
|
|
||||||
"period_end": 1000000000,
|
|
||||||
"period_start": 1000000000,
|
|
||||||
"post_payment_credit_notes_amount": 0,
|
|
||||||
"pre_payment_credit_notes_amount": 0,
|
|
||||||
"receipt_number": null,
|
|
||||||
"starting_balance": 0,
|
|
||||||
"statement_descriptor": null,
|
|
||||||
"status": "open",
|
|
||||||
"status_transitions": {
|
|
||||||
"finalized_at": 1000000000,
|
|
||||||
"marked_uncollectible_at": null,
|
|
||||||
"paid_at": null,
|
|
||||||
"voided_at": null
|
|
||||||
},
|
|
||||||
"subscription": null,
|
|
||||||
"subtotal": 5000,
|
|
||||||
"tax": null,
|
|
||||||
"tax_percent": null,
|
|
||||||
"total": 5000,
|
|
||||||
"total_tax_amounts": [],
|
|
||||||
"webhooks_delivered_at": 1000000000
|
|
||||||
}
|
|
||||||
@@ -1,102 +0,0 @@
|
|||||||
{
|
|
||||||
"data": [
|
|
||||||
{
|
|
||||||
"account_country": "US",
|
|
||||||
"account_name": "Dev account",
|
|
||||||
"amount_due": 5000,
|
|
||||||
"amount_paid": 0,
|
|
||||||
"amount_remaining": 5000,
|
|
||||||
"application_fee_amount": null,
|
|
||||||
"attempt_count": 0,
|
|
||||||
"attempted": false,
|
|
||||||
"auto_advance": false,
|
|
||||||
"billing": "charge_automatically",
|
|
||||||
"billing_reason": "manual",
|
|
||||||
"charge": null,
|
|
||||||
"collection_method": "charge_automatically",
|
|
||||||
"created": 1000000000,
|
|
||||||
"currency": "usd",
|
|
||||||
"custom_fields": null,
|
|
||||||
"customer": "cus_NORMALIZED0001",
|
|
||||||
"customer_address": null,
|
|
||||||
"customer_email": "hamlet@zulip.com",
|
|
||||||
"customer_name": null,
|
|
||||||
"customer_phone": null,
|
|
||||||
"customer_shipping": null,
|
|
||||||
"customer_tax_exempt": "none",
|
|
||||||
"customer_tax_ids": [],
|
|
||||||
"default_payment_method": null,
|
|
||||||
"default_source": null,
|
|
||||||
"default_tax_rates": [],
|
|
||||||
"description": "",
|
|
||||||
"discount": null,
|
|
||||||
"due_date": 1000000000,
|
|
||||||
"ending_balance": 0,
|
|
||||||
"footer": null,
|
|
||||||
"hosted_invoice_url": "https://pay.stripe.com/invoice/invst_NORMALIZED0000000000000002",
|
|
||||||
"id": "in_NORMALIZED00000000000002",
|
|
||||||
"invoice_pdf": "https://pay.stripe.com/invoice/invst_NORMALIZED0000000000000002/pdf",
|
|
||||||
"lines": {
|
|
||||||
"data": [
|
|
||||||
{
|
|
||||||
"amount": 5000,
|
|
||||||
"currency": "usd",
|
|
||||||
"description": null,
|
|
||||||
"discountable": true,
|
|
||||||
"id": "ii_NORMALIZED00000000000003",
|
|
||||||
"invoice_item": "ii_NORMALIZED00000000000003",
|
|
||||||
"livemode": false,
|
|
||||||
"metadata": {},
|
|
||||||
"object": "line_item",
|
|
||||||
"period": {
|
|
||||||
"end": 1000000000,
|
|
||||||
"start": 1000000000
|
|
||||||
},
|
|
||||||
"plan": null,
|
|
||||||
"proration": false,
|
|
||||||
"quantity": 1,
|
|
||||||
"subscription": null,
|
|
||||||
"tax_amounts": [],
|
|
||||||
"tax_rates": [],
|
|
||||||
"type": "invoiceitem"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"has_more": false,
|
|
||||||
"object": "list",
|
|
||||||
"total_count": 1,
|
|
||||||
"url": "/v1/invoices/in_NORMALIZED00000000000002/lines"
|
|
||||||
},
|
|
||||||
"livemode": false,
|
|
||||||
"metadata": {},
|
|
||||||
"next_payment_attempt": null,
|
|
||||||
"number": "NORMALI-0002",
|
|
||||||
"object": "invoice",
|
|
||||||
"paid": false,
|
|
||||||
"payment_intent": "pi_1F96BhGh0CmXqmnwXdcsnxHY",
|
|
||||||
"period_end": 1000000000,
|
|
||||||
"period_start": 1000000000,
|
|
||||||
"post_payment_credit_notes_amount": 0,
|
|
||||||
"pre_payment_credit_notes_amount": 0,
|
|
||||||
"receipt_number": null,
|
|
||||||
"starting_balance": 0,
|
|
||||||
"statement_descriptor": null,
|
|
||||||
"status": "open",
|
|
||||||
"status_transitions": {
|
|
||||||
"finalized_at": 1000000000,
|
|
||||||
"marked_uncollectible_at": null,
|
|
||||||
"paid_at": null,
|
|
||||||
"voided_at": null
|
|
||||||
},
|
|
||||||
"subscription": null,
|
|
||||||
"subtotal": 5000,
|
|
||||||
"tax": null,
|
|
||||||
"tax_percent": null,
|
|
||||||
"total": 5000,
|
|
||||||
"total_tax_amounts": [],
|
|
||||||
"webhooks_delivered_at": 1000000000
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"has_more": false,
|
|
||||||
"object": "list",
|
|
||||||
"url": "/v1/invoices"
|
|
||||||
}
|
|
||||||
@@ -1,102 +0,0 @@
|
|||||||
{
|
|
||||||
"data": [
|
|
||||||
{
|
|
||||||
"account_country": "US",
|
|
||||||
"account_name": "Dev account",
|
|
||||||
"amount_due": 5000,
|
|
||||||
"amount_paid": 0,
|
|
||||||
"amount_remaining": 5000,
|
|
||||||
"application_fee_amount": null,
|
|
||||||
"attempt_count": 1,
|
|
||||||
"attempted": true,
|
|
||||||
"auto_advance": false,
|
|
||||||
"billing": "charge_automatically",
|
|
||||||
"billing_reason": "manual",
|
|
||||||
"charge": "ch_NORMALIZED00000000000002",
|
|
||||||
"collection_method": "charge_automatically",
|
|
||||||
"created": 1000000000,
|
|
||||||
"currency": "usd",
|
|
||||||
"custom_fields": null,
|
|
||||||
"customer": "cus_NORMALIZED0001",
|
|
||||||
"customer_address": null,
|
|
||||||
"customer_email": "hamlet@zulip.com",
|
|
||||||
"customer_name": null,
|
|
||||||
"customer_phone": null,
|
|
||||||
"customer_shipping": null,
|
|
||||||
"customer_tax_exempt": "none",
|
|
||||||
"customer_tax_ids": [],
|
|
||||||
"default_payment_method": null,
|
|
||||||
"default_source": null,
|
|
||||||
"default_tax_rates": [],
|
|
||||||
"description": "",
|
|
||||||
"discount": null,
|
|
||||||
"due_date": 1000000000,
|
|
||||||
"ending_balance": 0,
|
|
||||||
"footer": null,
|
|
||||||
"hosted_invoice_url": "https://pay.stripe.com/invoice/invst_NORMALIZED0000000000000002",
|
|
||||||
"id": "in_NORMALIZED00000000000002",
|
|
||||||
"invoice_pdf": "https://pay.stripe.com/invoice/invst_NORMALIZED0000000000000002/pdf",
|
|
||||||
"lines": {
|
|
||||||
"data": [
|
|
||||||
{
|
|
||||||
"amount": 5000,
|
|
||||||
"currency": "usd",
|
|
||||||
"description": null,
|
|
||||||
"discountable": true,
|
|
||||||
"id": "ii_NORMALIZED00000000000003",
|
|
||||||
"invoice_item": "ii_NORMALIZED00000000000003",
|
|
||||||
"livemode": false,
|
|
||||||
"metadata": {},
|
|
||||||
"object": "line_item",
|
|
||||||
"period": {
|
|
||||||
"end": 1000000000,
|
|
||||||
"start": 1000000000
|
|
||||||
},
|
|
||||||
"plan": null,
|
|
||||||
"proration": false,
|
|
||||||
"quantity": 1,
|
|
||||||
"subscription": null,
|
|
||||||
"tax_amounts": [],
|
|
||||||
"tax_rates": [],
|
|
||||||
"type": "invoiceitem"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"has_more": false,
|
|
||||||
"object": "list",
|
|
||||||
"total_count": 1,
|
|
||||||
"url": "/v1/invoices/in_NORMALIZED00000000000002/lines"
|
|
||||||
},
|
|
||||||
"livemode": false,
|
|
||||||
"metadata": {},
|
|
||||||
"next_payment_attempt": null,
|
|
||||||
"number": "NORMALI-0002",
|
|
||||||
"object": "invoice",
|
|
||||||
"paid": false,
|
|
||||||
"payment_intent": "pi_1F96BhGh0CmXqmnwXdcsnxHY",
|
|
||||||
"period_end": 1000000000,
|
|
||||||
"period_start": 1000000000,
|
|
||||||
"post_payment_credit_notes_amount": 0,
|
|
||||||
"pre_payment_credit_notes_amount": 0,
|
|
||||||
"receipt_number": null,
|
|
||||||
"starting_balance": 0,
|
|
||||||
"statement_descriptor": null,
|
|
||||||
"status": "open",
|
|
||||||
"status_transitions": {
|
|
||||||
"finalized_at": 1000000000,
|
|
||||||
"marked_uncollectible_at": null,
|
|
||||||
"paid_at": null,
|
|
||||||
"voided_at": null
|
|
||||||
},
|
|
||||||
"subscription": null,
|
|
||||||
"subtotal": 5000,
|
|
||||||
"tax": null,
|
|
||||||
"tax_percent": null,
|
|
||||||
"total": 5000,
|
|
||||||
"total_tax_amounts": [],
|
|
||||||
"webhooks_delivered_at": 1000000000
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"has_more": false,
|
|
||||||
"object": "list",
|
|
||||||
"url": "/v1/invoices"
|
|
||||||
}
|
|
||||||
@@ -1,102 +0,0 @@
|
|||||||
{
|
|
||||||
"data": [
|
|
||||||
{
|
|
||||||
"account_country": "US",
|
|
||||||
"account_name": "Dev account",
|
|
||||||
"amount_due": 5000,
|
|
||||||
"amount_paid": 0,
|
|
||||||
"amount_remaining": 5000,
|
|
||||||
"application_fee_amount": null,
|
|
||||||
"attempt_count": 1,
|
|
||||||
"attempted": true,
|
|
||||||
"auto_advance": false,
|
|
||||||
"billing": "charge_automatically",
|
|
||||||
"billing_reason": "manual",
|
|
||||||
"charge": "ch_NORMALIZED00000000000002",
|
|
||||||
"collection_method": "charge_automatically",
|
|
||||||
"created": 1000000000,
|
|
||||||
"currency": "usd",
|
|
||||||
"custom_fields": null,
|
|
||||||
"customer": "cus_NORMALIZED0001",
|
|
||||||
"customer_address": null,
|
|
||||||
"customer_email": "hamlet@zulip.com",
|
|
||||||
"customer_name": null,
|
|
||||||
"customer_phone": null,
|
|
||||||
"customer_shipping": null,
|
|
||||||
"customer_tax_exempt": "none",
|
|
||||||
"customer_tax_ids": [],
|
|
||||||
"default_payment_method": null,
|
|
||||||
"default_source": null,
|
|
||||||
"default_tax_rates": [],
|
|
||||||
"description": "",
|
|
||||||
"discount": null,
|
|
||||||
"due_date": 1000000000,
|
|
||||||
"ending_balance": 0,
|
|
||||||
"footer": null,
|
|
||||||
"hosted_invoice_url": "https://pay.stripe.com/invoice/invst_NORMALIZED0000000000000002",
|
|
||||||
"id": "in_NORMALIZED00000000000002",
|
|
||||||
"invoice_pdf": "https://pay.stripe.com/invoice/invst_NORMALIZED0000000000000002/pdf",
|
|
||||||
"lines": {
|
|
||||||
"data": [
|
|
||||||
{
|
|
||||||
"amount": 5000,
|
|
||||||
"currency": "usd",
|
|
||||||
"description": null,
|
|
||||||
"discountable": true,
|
|
||||||
"id": "ii_NORMALIZED00000000000003",
|
|
||||||
"invoice_item": "ii_NORMALIZED00000000000003",
|
|
||||||
"livemode": false,
|
|
||||||
"metadata": {},
|
|
||||||
"object": "line_item",
|
|
||||||
"period": {
|
|
||||||
"end": 1000000000,
|
|
||||||
"start": 1000000000
|
|
||||||
},
|
|
||||||
"plan": null,
|
|
||||||
"proration": false,
|
|
||||||
"quantity": 1,
|
|
||||||
"subscription": null,
|
|
||||||
"tax_amounts": [],
|
|
||||||
"tax_rates": [],
|
|
||||||
"type": "invoiceitem"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"has_more": false,
|
|
||||||
"object": "list",
|
|
||||||
"total_count": 1,
|
|
||||||
"url": "/v1/invoices/in_NORMALIZED00000000000002/lines"
|
|
||||||
},
|
|
||||||
"livemode": false,
|
|
||||||
"metadata": {},
|
|
||||||
"next_payment_attempt": null,
|
|
||||||
"number": "NORMALI-0002",
|
|
||||||
"object": "invoice",
|
|
||||||
"paid": false,
|
|
||||||
"payment_intent": "pi_1F96BhGh0CmXqmnwXdcsnxHY",
|
|
||||||
"period_end": 1000000000,
|
|
||||||
"period_start": 1000000000,
|
|
||||||
"post_payment_credit_notes_amount": 0,
|
|
||||||
"pre_payment_credit_notes_amount": 0,
|
|
||||||
"receipt_number": null,
|
|
||||||
"starting_balance": 0,
|
|
||||||
"statement_descriptor": null,
|
|
||||||
"status": "open",
|
|
||||||
"status_transitions": {
|
|
||||||
"finalized_at": 1000000000,
|
|
||||||
"marked_uncollectible_at": null,
|
|
||||||
"paid_at": null,
|
|
||||||
"voided_at": null
|
|
||||||
},
|
|
||||||
"subscription": null,
|
|
||||||
"subtotal": 5000,
|
|
||||||
"tax": null,
|
|
||||||
"tax_percent": null,
|
|
||||||
"total": 5000,
|
|
||||||
"total_tax_amounts": [],
|
|
||||||
"webhooks_delivered_at": 1000000000
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"has_more": false,
|
|
||||||
"object": "list",
|
|
||||||
"url": "/v1/invoices"
|
|
||||||
}
|
|
||||||
@@ -1,219 +0,0 @@
|
|||||||
{
|
|
||||||
"data": [
|
|
||||||
{
|
|
||||||
"account_country": "US",
|
|
||||||
"account_name": "Dev account",
|
|
||||||
"amount_due": 5000,
|
|
||||||
"amount_paid": 5000,
|
|
||||||
"amount_remaining": 0,
|
|
||||||
"application_fee_amount": null,
|
|
||||||
"attempt_count": 1,
|
|
||||||
"attempted": true,
|
|
||||||
"auto_advance": false,
|
|
||||||
"billing": "charge_automatically",
|
|
||||||
"billing_reason": "manual",
|
|
||||||
"charge": "ch_NORMALIZED00000000000003",
|
|
||||||
"collection_method": "charge_automatically",
|
|
||||||
"created": 1000000000,
|
|
||||||
"currency": "usd",
|
|
||||||
"custom_fields": null,
|
|
||||||
"customer": "cus_NORMALIZED0001",
|
|
||||||
"customer_address": null,
|
|
||||||
"customer_email": "hamlet@zulip.com",
|
|
||||||
"customer_name": null,
|
|
||||||
"customer_phone": null,
|
|
||||||
"customer_shipping": null,
|
|
||||||
"customer_tax_exempt": "none",
|
|
||||||
"customer_tax_ids": [],
|
|
||||||
"default_payment_method": null,
|
|
||||||
"default_source": null,
|
|
||||||
"default_tax_rates": [],
|
|
||||||
"description": "",
|
|
||||||
"discount": null,
|
|
||||||
"due_date": 1000000000,
|
|
||||||
"ending_balance": 0,
|
|
||||||
"footer": null,
|
|
||||||
"hosted_invoice_url": "https://pay.stripe.com/invoice/invst_NORMALIZED0000000000000002",
|
|
||||||
"id": "in_NORMALIZED00000000000002",
|
|
||||||
"invoice_pdf": "https://pay.stripe.com/invoice/invst_NORMALIZED0000000000000002/pdf",
|
|
||||||
"lines": {
|
|
||||||
"data": [
|
|
||||||
{
|
|
||||||
"amount": 5000,
|
|
||||||
"currency": "usd",
|
|
||||||
"description": null,
|
|
||||||
"discountable": true,
|
|
||||||
"id": "ii_NORMALIZED00000000000003",
|
|
||||||
"invoice_item": "ii_NORMALIZED00000000000003",
|
|
||||||
"livemode": false,
|
|
||||||
"metadata": {},
|
|
||||||
"object": "line_item",
|
|
||||||
"period": {
|
|
||||||
"end": 1000000000,
|
|
||||||
"start": 1000000000
|
|
||||||
},
|
|
||||||
"plan": null,
|
|
||||||
"proration": false,
|
|
||||||
"quantity": 1,
|
|
||||||
"subscription": null,
|
|
||||||
"tax_amounts": [],
|
|
||||||
"tax_rates": [],
|
|
||||||
"type": "invoiceitem"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"has_more": false,
|
|
||||||
"object": "list",
|
|
||||||
"total_count": 1,
|
|
||||||
"url": "/v1/invoices/in_NORMALIZED00000000000002/lines"
|
|
||||||
},
|
|
||||||
"livemode": false,
|
|
||||||
"metadata": {},
|
|
||||||
"next_payment_attempt": null,
|
|
||||||
"number": "NORMALI-0002",
|
|
||||||
"object": "invoice",
|
|
||||||
"paid": true,
|
|
||||||
"payment_intent": "pi_1F96BhGh0CmXqmnwXdcsnxHY",
|
|
||||||
"period_end": 1000000000,
|
|
||||||
"period_start": 1000000000,
|
|
||||||
"post_payment_credit_notes_amount": 0,
|
|
||||||
"pre_payment_credit_notes_amount": 0,
|
|
||||||
"receipt_number": null,
|
|
||||||
"starting_balance": 0,
|
|
||||||
"statement_descriptor": null,
|
|
||||||
"status": "paid",
|
|
||||||
"status_transitions": {
|
|
||||||
"finalized_at": 1000000000,
|
|
||||||
"marked_uncollectible_at": null,
|
|
||||||
"paid_at": 1000000000,
|
|
||||||
"voided_at": null
|
|
||||||
},
|
|
||||||
"subscription": null,
|
|
||||||
"subtotal": 5000,
|
|
||||||
"tax": null,
|
|
||||||
"tax_percent": null,
|
|
||||||
"total": 5000,
|
|
||||||
"total_tax_amounts": [],
|
|
||||||
"webhooks_delivered_at": 1000000000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"account_country": "US",
|
|
||||||
"account_name": "Dev account",
|
|
||||||
"amount_due": 0,
|
|
||||||
"amount_paid": 0,
|
|
||||||
"amount_remaining": 0,
|
|
||||||
"application_fee_amount": null,
|
|
||||||
"attempt_count": 0,
|
|
||||||
"attempted": true,
|
|
||||||
"auto_advance": false,
|
|
||||||
"billing": "charge_automatically",
|
|
||||||
"billing_reason": "manual",
|
|
||||||
"charge": null,
|
|
||||||
"collection_method": "charge_automatically",
|
|
||||||
"created": 1000000000,
|
|
||||||
"currency": "usd",
|
|
||||||
"custom_fields": null,
|
|
||||||
"customer": "cus_NORMALIZED0001",
|
|
||||||
"customer_address": null,
|
|
||||||
"customer_email": "hamlet@zulip.com",
|
|
||||||
"customer_name": null,
|
|
||||||
"customer_phone": null,
|
|
||||||
"customer_shipping": null,
|
|
||||||
"customer_tax_exempt": "none",
|
|
||||||
"customer_tax_ids": [],
|
|
||||||
"default_payment_method": null,
|
|
||||||
"default_source": null,
|
|
||||||
"default_tax_rates": [],
|
|
||||||
"description": "",
|
|
||||||
"discount": null,
|
|
||||||
"due_date": 1000000000,
|
|
||||||
"ending_balance": 0,
|
|
||||||
"footer": null,
|
|
||||||
"hosted_invoice_url": "https://pay.stripe.com/invoice/invst_NORMALIZED0000000000000001",
|
|
||||||
"id": "in_NORMALIZED00000000000001",
|
|
||||||
"invoice_pdf": "https://pay.stripe.com/invoice/invst_NORMALIZED0000000000000001/pdf",
|
|
||||||
"lines": {
|
|
||||||
"data": [
|
|
||||||
{
|
|
||||||
"amount": 48000,
|
|
||||||
"currency": "usd",
|
|
||||||
"description": "Zulip Standard",
|
|
||||||
"discountable": false,
|
|
||||||
"id": "ii_NORMALIZED00000000000001",
|
|
||||||
"invoice_item": "ii_NORMALIZED00000000000001",
|
|
||||||
"livemode": false,
|
|
||||||
"metadata": {},
|
|
||||||
"object": "line_item",
|
|
||||||
"period": {
|
|
||||||
"end": 1000000000,
|
|
||||||
"start": 1000000000
|
|
||||||
},
|
|
||||||
"plan": null,
|
|
||||||
"proration": false,
|
|
||||||
"quantity": 6,
|
|
||||||
"subscription": null,
|
|
||||||
"tax_amounts": [],
|
|
||||||
"tax_rates": [],
|
|
||||||
"type": "invoiceitem"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"amount": -48000,
|
|
||||||
"currency": "usd",
|
|
||||||
"description": "Payment (Card ending in 4242)",
|
|
||||||
"discountable": false,
|
|
||||||
"id": "ii_NORMALIZED00000000000002",
|
|
||||||
"invoice_item": "ii_NORMALIZED00000000000002",
|
|
||||||
"livemode": false,
|
|
||||||
"metadata": {},
|
|
||||||
"object": "line_item",
|
|
||||||
"period": {
|
|
||||||
"end": 1000000000,
|
|
||||||
"start": 1000000000
|
|
||||||
},
|
|
||||||
"plan": null,
|
|
||||||
"proration": false,
|
|
||||||
"quantity": 1,
|
|
||||||
"subscription": null,
|
|
||||||
"tax_amounts": [],
|
|
||||||
"tax_rates": [],
|
|
||||||
"type": "invoiceitem"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"has_more": false,
|
|
||||||
"object": "list",
|
|
||||||
"total_count": 2,
|
|
||||||
"url": "/v1/invoices/in_NORMALIZED00000000000001/lines"
|
|
||||||
},
|
|
||||||
"livemode": false,
|
|
||||||
"metadata": {},
|
|
||||||
"next_payment_attempt": null,
|
|
||||||
"number": "NORMALI-0001",
|
|
||||||
"object": "invoice",
|
|
||||||
"paid": true,
|
|
||||||
"payment_intent": null,
|
|
||||||
"period_end": 1000000000,
|
|
||||||
"period_start": 1000000000,
|
|
||||||
"post_payment_credit_notes_amount": 0,
|
|
||||||
"pre_payment_credit_notes_amount": 0,
|
|
||||||
"receipt_number": null,
|
|
||||||
"starting_balance": 0,
|
|
||||||
"statement_descriptor": "Zulip Standard",
|
|
||||||
"status": "paid",
|
|
||||||
"status_transitions": {
|
|
||||||
"finalized_at": 1000000000,
|
|
||||||
"marked_uncollectible_at": null,
|
|
||||||
"paid_at": 1000000000,
|
|
||||||
"voided_at": null
|
|
||||||
},
|
|
||||||
"subscription": null,
|
|
||||||
"subtotal": 0,
|
|
||||||
"tax": null,
|
|
||||||
"tax_percent": null,
|
|
||||||
"total": 0,
|
|
||||||
"total_tax_amounts": [],
|
|
||||||
"webhooks_delivered_at": 1000000000
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"has_more": false,
|
|
||||||
"object": "list",
|
|
||||||
"url": "/v1/invoices"
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user