月間経常収益を$1000から$4000米ドルへ増加させる

月間経常利益(MRR)はSaaS企業にとって最も重要な基準になります。MRRを出来る限り上げていく必要があります。2月13日の時点でMRRは$1000でした。先週は$4000を超えました。 この7か月の間に何が起きたのでしょうか?最終週になぜMRRがこんなに増加したのでしょうか? 時間を経過させる事 大きな一つのポイントとして、時間を経過させて、徐々にユーザーを増やしていく事が重要です。グラフを見ると、突発したのは一回のみです。この7か月間のうちに、私は503ギットもコミットしました。 先週の突発 6月終わりにこちらへアクセスしたいユーザーと話しをし始めました。 JSON API to convert a lot of documents. They also wanted results to be normalised like this: [{ "raw": [ { "date": "03/08/20", "description": "Monthly Service Fee", "credit": null, "debit": "5.00", "balance": "777.07", "amount": null, "charge": null } ], "normalised": [ { "date": "03/08/20", "description": "Monthly Service Fee", "amount": "-5.00" } ] }] このお客様は様々な銀行明細をお持ちでしたので、サポートするのが大変でしたが、徐々に安定してきて、正確度も満足していただけました。先週からこのお客様は多額な自動引き落としへ登録してくれたのです。 変換結果の向上 ほとんどの503コミットは様々な銀行の変換結果向上に使いました。Regions Bankの銀行明細をアップロードし、適切に処理が出来ない場合、アプリを閉じてもう二度と使わないでしょう。 これを行う事により mole whacking 細かい仕事は大変ですが、売上に影響を与えます。 次: ローカリゼーション 現状、売上の大半は英語を言語とする国からきています。ただ、この銀行明細のPDFの問題というのは世界中で起きている問題だと思うのです。メキシコ、中国、日本、ドイツでなぜ売上を出せないのでしょうか?理由は私達のウェブサイトが英語なので、英語を言語にしない国はスペイン語、中国語、日本語、ドイツ語で検索を掛けるからでしょう。

Read more →

お客様が成功出来ない時

私の人生の中で、これがいつ始まったのか分かりませんが、最低な奴らが「カスタマーサービス」を「カスタマーサクセス」という名前へ変えました。読むと特に意味は無い為、結構変だと思います。知っている人は知っているのですが、「カスタマーサクセス」は「カスタマーサポート」を意味し、メッセージの返信、問題や質問があるお客様からのメールや電話対応をします。 数年前、仮想通貨交換の会社に勤務していた際、「カスタマーサクセス」というチームがありました。面白い話しですが、手っ取り早くいうと、ほとんどのお客様は成功に至っていませんでした。大金を失っていたからです。本当にお客様の成功を思っているのであれば持続的に失い続ける事を避ける為にも口座を閉めるべきだと、よく冗談で言っていたものです。 洋服の購入 先週、 H&Mから何枚か洋服を購入しようと思った。ウェブサイトへ行き、ログインしようとした。ログインが出来なかった。では結構です。パスワードのリセットを試みた。パスワードのリセットボタンを数時間に渡り何度か押したのだが、メールは届かなかった。とても面倒くさい。その後「カスタマーサクセス」チームへメールを送信し、ログイン又パスワードのリセットも出来ない事を伝えた。その後、私はログインせずに洋服をいくつか購入した。 翌日、電話が来た。「H&Mのカスタマーサクセスの(名前)です。今お話しできるでしょうか?」「はい、良いですよ」「パスワードのリセットが出来なかった様ですね。理由はメールアドレスが「ゲストアカウント」として登録されているからです。ゲストアカウントではログインやパスワードのリセットは出来ません」「という事は私は今までアカウントにログインをした事が無いという事ですか?」「そうです」 おかしな話しですね!システムにリミットがあり、問題を解決するのではなく、不満なお客様の対応はカスタマーサポートに任せっきりになっているのです。この様なバグはH&Mにも金銭的影響が出てるかもしれません。: ログインが出来ないと人々はいらだち、他のお店で洋服を購入します ほとんどのユーザーは匿名で洋服を購入する事になり、H&Mにとって購入履歴を把握できない事はターゲットを絞った広告を行う事が難しくなるという事になります その他理由 このバグがH&Mへどの位の金銭的影響を及ぼしているのかを分析する事は難しいと思いますが、もしかしたらこのバグがH&Mへ金銭的利益をもたらす可能性もあります。気づいた事は、お客様から入手したバグを修正すれば、サブスクリプションを購入をし、仲間たちへアプリをお勧めしてしてくれうかもしれません。 H&M様、もしこのブログ投稿を読んでいるのであれば、収益の中の少額との交換で、喜んでこのバグの対応をします!

ウェーブベースのセキュリティ

このアプリの仕事は2021年から借りている共有オフィスから大体行っています。ルールとしては皆、ロビーにある受付デスクでチェックインし、上の階へ上がっていきます。 月日が経つと、私は受付でチェックインせずに入る様になりました。皆さんの想像通り、素晴らしかったです。ビルに入り、歩いて、上に上がり、デスクに行って座れば良いのです。完璧ですね。 ただし、この都合の良さはそんなには長続きしませんでした。ロビーで警備員に止められる様になりました。「上の階へ上がる前にチェックインをしてください」。面倒です。私のアレンジメントが崩されてしまいました。ルールに厳しい警備員がいる時だけ私はチェックインをし、いない時は、直接上の階へ行っていました。 その後、他の警備員からもチェックインをする様に言われ始めました。結構です。私はチェックインする様になりました。少しすると私は入り口から入る際に受付係に手を振る様になりました。彼らも手を振り返してくれます。これでチェックインした事になりますね?警備員曰くなるそうなので、これでアクセスの許可が下りました。

シンプルな機能の裏に掛かる労働時間

このウェブサイトを2021年4月から開始し、ユーザーはパスワード保護されいるPDFファイルをアップロードをアップロードしたい事は分かっていた。これらのPDFのサポートを行う事は面倒だった為、手を付けなかった。ただ、最近、ユーザーからパスワード保護が掛かったPDFを対応してくれないかと依頼があった為、やる事にした。 これはシンプルな機能で結構うまくいきます。昨日はほとんど丸一日作業をしていて、今朝午前2:32まで仕事をしていました。この数日収集した少量のデータによると、約5%のユーザーがパスワード付のPDFをアップロードしている事が分かった。今までは画面にエラーメッセージが出る前にアップロードする事自体却下していた。これらのほとんどのユーザーは、ウェブサイト自体上手くいかないと想定し、どこかへ消えてしまった。 この機能を実現するに辺り掛かった時間には驚き、不満を感じたので、あなたが私に感謝出来る様、私があなたの為に行った内容を共有します。1 土曜日 午後11:46 – ファイルマッピングテーブルにフィールドを追加する。 ファイルマッピングテーブルはユーザーがアップロードした各PDFの情報を保存する。2つのフィールドパスワード と パスワード必須を追加した。 このアイディアはパスワードやその他書類のメタデータをデータベースへ保存する事だ。 日曜日 午前11:10 – APIが保護されたPDFを受け取った場合 APIを変えて、ファイルマッピング を作成し、パスワード必須で記録し、PDF種類は不明とした。 以前まではPDFはTEXT_BASED やIMAGE_BASEDで対応する事ができた。パスワード無しではファイルを読むことは出来ないので、TEXT_BASED か IMAGE_BASEDか分析し決定する事も出来ない。 これをする理由はテキストかイメージのPDFによってコードが別になるからだ。 日曜日 午前1:14 - SQLを書き、ファイルマッピングレコードをアップロード UPDATE file_mapping SET password = ? WHERE uuid = ?; 日曜日 午後 2:55 – SQLを変更し、APIの作成をし書類のパスワードを設定 UPDATE file_mapping SET password = ? pdf_type = ? page_count = ? WHERE uuid = ?; APIのコーディングを行い、ユーザーがパスワード設定をする事に成功した後、フィールドマッピングのフィールドを変えたいと思い始めた。書類を読んだ後、PDF種類を分類し、何ページあるかカウントするが可能となる。 val fileMappings = repository.getFileMappings(body.passwords.map { it.uuid }) val updates = mutableMapOf<String, UpdateFileMapping>() val results = fileMappings.

Read more →

PDF銀行明細JSON API

1. 認可トークンの取得 Google Chrome上でこのサイトへアクセス https://bankstatementconverter.com/ ログイン F12を押し、デベロッパーのツールを開く 「Application」タブをクリックする 左のパネルから「Storage」→ 「Local Storage」 →"https://bankstatementconverter.com/" 「bsc-authToken」と呼ばれるキーが出るので、相応値を入力する この値が認可トークンになります。値をコピーしてください。 2. PDF APIのアップロード 方法: 投稿 URL: https://api2.bankstatementconverter.com/api/v1/BankStatement ヘッダー: { Authorization: AUTH_TOKEN } ボディー: Multipart Form Data ヘッダー依頼 ボディー依頼 ボディー回答 3. PDF APIの変換 方法: 投稿 URL: https://api2.bankstatementconverter.com/api/v1/BankStatement/convert?format=JSON ヘッダー: { Authorization: AUTH_TOKEN } ボディー: JSONの中にUUIDストリングのリストあり。PDF APIのアップロードを行う事によりUUIDの返答が来る。``` ヘッダー依頼 ボディー依頼 ぼできー返答 ボディー内で提供した各UUIDにて、書類の配列変換の結果を得る事ができる。各結果は整列されており、正規化されたプロパティである。正規化されたプロパティから全ての取引コラムが提供される。正規化されたプロパティは日付だけではなく、内容と金額のコラムも含まれている。下記の例から分かる様に、デビット金額 「5.00」は正規化すると価格は「-5.00」となる。 [{ "raw": [ { "date": "03/08/20", "description": "Monthly Service Fee", "credit": null, "debit": "5.00", "balance": "777.

Read more →

HSBCのもぐら叩きの発展

先週、お客様から数百枚の書類処理を手伝って欲しいと聞かれました。2018年から始まるいくつかの銀行口座のPDFを持っていました。価格順に整え、彼の書類の処理を始めました。 私のアイディアとしては銀行明細コンバーター(BSC)のPDFをCSVプロセッサーへ変換して処理をしようと思ったのです。ただ、いくつか問題に直面しました。BSCは一般的なアルゴリズムを使い、PDFの取引記録から探知していきます。これは通常うまくいき、ほとんどの銀行はうまく使えます。ただ、大量の明細の場合一般的なアルゴリズムは通用しません。この解決策として、書類の種類を探知し、その後カスタムコードを通す必要があります。 ほとんどのお客様から届く報告は何らかの解析エラーとなってしまうとの事でした。なので、私は彼らにどの書類が原因で解析エラーとなってしまうのかを聞きました。この工程を私は「もぐら叩き」の発展と呼んでいます。このブログ投稿では、私のHSBCの銀行明細の為の素晴らしいカスタムプロセッサーの作成工程を皆さんと一緒に楽しみながら確認していこうと思います。 今はどの様になっているのか? 現状は結構ひどい状態です!内容も不均等な状態です。理由は分かりませんが日付の数字が無い状態です。日付の年の部分がありません。結果も2つのテーブルに分かれてしまいます。為替値もありません。これらの各問題に関して解決策を確認していきましょう。 内容が不均等 この件に関してはまだ対応が簡単です。内容を統合する場合、いくつかのラインを跨ぎ、新しいラインのキャラクターを作成します。ただ、そうではなく、スペースキャラクターを追加する法が良いと思うのです。そうすればCSVのラインが不均等になる事はありません。これをHSBCだけではなく、全ての書類に適応させます なぜか日付の数値が無い これはHSBCの銀行明細の特色です。同日に一回以上の取引がある場合、最初に取引した日付しかプリントされません。ちょっと不便ですね。これでは明細も簡単に見る事が出来ません。この私PDFキングとしての生活までも難しくさせます。ただ、解決するのはそんなに難しい事ではなく、日付情報がなければ加工の記録から日付情報をコピーする事ができます。 結果が2つのテーブルに分割されてしまう HKD普通預金口座と私の米ドル、オーストラリアドルの外貨口座が分割されてしまいます。外貨口座には「CCY」のコラムが追加であります。この場合は頭の良い一般のアルゴリズムは同じヘッダーを同じテーブルに統合してしまうのです。もしも、ヘッダーが違う場合、テーブルも別々で出てきます。この問題はHKD普通預金口座に「CCY」のコラムを追加する事で解決する事が出来た。 ###為替値が無い 日付の数値が無かった時と同じ解決法で対応する事が出来る 年の数値が無い 以前ブログへ投稿したアドバンステクニックで明細日を確かめる事ができる。コードを作ってみましょう。 内容が不均等 RowMerger.ktを変更する事により、セパレーターストリングが許可をします。その後、スペースキャラクターを入れます。結構シンプルですね。 日付と為替値が無い 以前の書類でもこの問題に対応してきました。この二つの解決法は下記から閲覧できます。 結果が2つのテーブルに分割してしまう この問題の解決法には私はあまり満足していません。基本的に私が行った事は「CCY」コラムが無いテーブルヘッダーにテーブルヘッダーを追加しただけだからです。これを行うと書類全てのテーブルヘッダーが同じになります。これは確実にハックです。gridTransform方法を使った方が良いかもしれません。 日付に年が含まれていない 2つのステップにより解決する事が出来ました。最初に明細日を捕獲する為にコードを書きました。 その後、enrichDatesを使用し、全ての日付数値の日付コラムを変換しました。以前のブログ投稿で日付濃縮コードの話しを少ししました。 previous blog post. 結論 見た目も大分向上しました。ただ、まだ完璧とは言えません。いくつかのCCYコラムの数値がまだ白欄になっています。これらをHKDに設定した方が良いかもしれません。それと、残高のコラムもギャップが出来ています。この理由はHSBCの明細は日々の残高は一回しか記載されないからです。この数値を計算する事は可能です。ただ、これを行うのは色々面倒そうです。

何年でしたっけ?

最近よく耳にするのが、CSVへ変換された銀行明細の年が間違えているという苦情です。最初この苦情を耳にした時、「何のことを言っているのか?取引データを探して、CSVファイルへ書き込むだけの事だ。なぜ年に間違えが発生するのか?」と思いました。まず私のHSBC銀行明細の一つを見ながらステップを辿っていきましょう。 変換後はこの様になります ダウンロードし、開くとテキストエディターに下記の様なサブライムテキストが出てきます。 Date,Transaction Details,Deposit,Withdrawal,Balance 20 Feb,B/F BALANCE,,,"XXX" 27 Feb,CREDIT INTEREST,3.84,,"XXX" これをエクセルで開くとこの様になります。 画像の上部の日付に注目ください。年の設定は2022年になっていますが、CSVには年が含まれていません。エクセルは意地悪なので、今年の年が入力されてしまうのです。 私達の問題では無いのか? 私のお客様たちに言えるのは「エクセルが自動的に年を入力してしまいます。こちらが作成したCSVの情報は合っているのですが、問題はエクセルにある」という事です。銀行明細の適切な年を掲載する方法が必ずあるはずなのです。私のHSBC銀行の明細の上部を見てみましょう。 ミステリアスなラベル無しの日付「20 March 2021」が出ています。良く分かりませんが、この日は「明細書の日付」または「明細書作成日」の日付だとしましょう。今持っているデータを元に適切な年を追加する方法があるか試してみましょう。 例 #1 – 3月 Input Statement Date: 20 March 2021 Dates: 20 Feb, 27 Feb, 2 Mar, 4 Mar, 9 Mar, 12 Mar, 16 Mar Output 20 Feb 2021 27 Feb 2021 2 Mar 2021 4 Mar 2021 9 Mar 2021 12 Mar 2021 16 Mar 2021 これは簡単にできると思います。明細の日付を引っ張ってきて追加してみましょう。 Example #2 – 1月 Input

Read more →

自動的に銀行取引を行う

ある日、銀行明細コンバーターがどの様な問題を解決してくれるかについて少し明確になりました。単純な答えは「銀行明細のPDFから抜き取られた問題を解決する事」です。それは真実ですが、それと共に一般的な問題を解決する事が出来きます。「ユーザーが銀行取引データへアクセスする事が可能となる。」ただ、使い方がやや難しいです。2021年の取引データを閲覧したい場合、下記を行う必要があります。 オンラインバンキングへログイン 各口座12ヵ月分のPDFをダウンロード bankstatementconverter.comへアクセス 12個のファイルをアップロードする コンバートをクリック これらのいくつかのステップを省く事が出来たら楽になるのではないでしょうか?これを達成する為に私は方法を考えたのです。 アプローチ #1 – ドライビングブラウザ拡張 ブラウザ拡張を作成し、ウェブブラウザのコントロールや銀行からのPDFを自動的にダウンロードする事が可能になります。ユーザーは下記を行う必要があります。 オンラインバンキングへログイン ブラウザ拡張に書類を引っ張る ブラウザ拡張がリンクをクリックし、ボタンを押し、PDFを取得するのに必要な箇所に入力される。 この解決法の良い点としてはユーザーがオンラインバンキングに必要なクレデンシャル情報を入力する必要が無いのです。拡張機能は悪意がある物でないかを確認する為第三の業者が監査を行います。 悪い点に関しては全てのオンラインバンキングプラットフォームのインテグレーションを構築する必要がある事です。これに関しては「一般的な解決法」は特にありません。 悲しい事に、これを行う事は技術的に不可能です。まだ確認は取れていませんが、おそらくウェブブラウザは第三者がブラウザ拡張機能を「ドライブ」する事を認めていません。 アプローチ #2 – ブラウザ拡張ガイド 前回のアプローチとかなり似ているが、今回違う所はブラウザ拡張自体がユーザーに何をすれば良いかをおしえてくれる事です。「ここをクリック」、「XYZを入力」、「このボタンをクリック」。面倒ですが、手助けになると思います。 これをする事で可能となりますが、ユーザーが使いやすいか使いにくいと感じるかは分かりません。 アプローチ #3 – ドライブデスクトップアプリケーション このアプローチは#1と似た様な感じですが、ブラウザ拡張では無くWindows/MacOSアプリケーションを使用する所のみ違いがあります。ブラウザ拡張でページを操作する事が出来ない場合、ユーザーのマウス/キーボードをコントロールするデスクトップアプリケーションを使用する事ができます。作成するのがかなり難しく、実際どの様に対応できるかは分かりませんが、可能であると感じています。このアプローチの良い点は、ユーザーがクレデンシャル情報を提供する必要が無く、全てユーザーのパソコンで操作される為、銀行側がこのアプリケーションを阻止する事は出来ません。 アプローチ #4 – APIの完全自動化 APIを作成し、オンラインバンキングへのログインを可能にする工程で、PDFの明細ダウンロード、取得が可能となります。下記の様に出来ると想定しています: ユーザーが銀行のクレデンシャル情報を私達へ提供する 私達のサーバーが銀行のAPIへ連絡をし、銀行明細のPDFをダウンロードする 私達のサーバーが銀行明細をPDFからCSVへ変換する ユーザーへCSVを提供する このアプローチで良い点はユーザーがやらなければいけない事がほんの少ししか無いという事です。ダウンロードは結構な量になります!理由は分かりませんが、銀行は第三者がオンラインバンキングポータルからデータを入手する事を嫌う為、この様な行動には様々な防塞を仕掛けてきます。この様々な防塞を避けていくことが大変な仕事となります。銀行が納得し、インテグレーションをしてくれるまでは多くの確認作業が必要になります。 どの様にインテグレーションを構築するか? 私は2つの口座を持っています。という事は2つの銀行口座のデーターを抽出する為にコードを作成する必要があります。残念な事に(このアプリの目的の為)、世界中には2つの銀行以上の銀行が存在します。いくつかの方法があります。 私が口座を保持していない銀行のインテグレーションをどの様に作成すれば良いでしょうか。: 各銀行のデベロッパーに支払いをし、インテグレーションを構築してもらう ツールを作成し、デベロッパーでは無いユーザーに彼ら自身の銀行のインテグレーションを構築してもらう 世界中全ての銀行の口座を開く 人々へ支払いをした上、銀行口座へのアクセスを許可してもらい、インテグレーションを構築する これは大きな懸念点であり、どうアプローチすべきであるか分かりません。現状この件に関してはそれほど考えていません。まずは、私自身2つの銀行口座があるので、まずそのインテグレーションの構築を開始する事が出来ます。

取引表のヘッダーを探知する

今回で銀行明細変換をどの様に行うのかの説明をするのは2回目の投稿となります。 previous articleこの投稿内で、文字の抽出とPDFのバウンディングボックスに関する件は説明をしました。今回の投稿では私がどの様に文字やバウンディングボックスを使い、取引表のヘッダーを探知するのかについて説明をします。 val pageRegion = Rectangle(0f, 0f, page.cropBox.width, page.cropBox.height) val lines = LineExtractor(page).extractLines() val headers = headerDetector.detect(allCharacters, lines, pageRegion).map(::headerTransformer) 上記のコードは、ページ数、ページのグラフィカルラインの探知方法は取引ヘッダー探知クラスを参考にしてください。グラフィカルラインは通常、表のヘッダーの質を向上させますが、この詳細に関してはまた後で話し合いましょう。 override fun detect(__unsortedCharacters: List<CharAndBound>, graphicalLines: List<Line>, pageRegion: Rectangle): List<TableHeader> { val words = merger.merge(__unsortedCharacters) ... } まず、文字のリストを言葉のリストに併合していきます。これを行うには、文字をY、X位置へ並び替え、文字間の水平、垂直を測定します。どちらの数値も特定なしきい値を超すと新しい単語となります。こちらがCharacterMerger.ktからの面白いコード であり、単語間の境界を探知します。 val xDistance = current.bound.left() - previous.bound.right() val yDistance = Math.abs(current.bound.bottom() - previous.bound.bottom()) val isCloseEnoughOnX = xDistance <= xThreshold val isCloseEnoughOnY = yDistance <= yThreshold if (!isCloseEnoughOnY || !isCloseEnoughOnX) { words.add(createTextAndBound(buffer)) buffer.clear() } その後、単語をライン内へ構造し、各ラインの銀行明細取引表のヘッダーの様な見た目になっているかを確認します。もう一つヘッダーがあればリターンします。それを怠ると、ヘッダーに2つ線が出てきてしまいます。上手くいくと良いですね!上手くいかなければ、ヘッダーが3本になっていないか確認をしてみて下さい。これはあまりきれいな解決策ではありませんが、実際には上手くいくことが多いです。それに、これはパフォーマンスが重要ではない為、必要な時に「ライン探知」法を使えば良いのです。

Read more →

PDFからテキストとバウンディングボックスを抽出する

今回のブログ投稿のシリーズは銀行明細変換で以前説明をした続きになります。この投稿ではバウンディングボックスとその他の特徴に関するコードを説明します。大半のコードは下記から取り出してきました。 DrawPrintTextLocations and PDFTextStripper. まず初めに、これを行うのは PDFBoxを使用してPDFファイルをアップロードする その後、一ページずつ処理をしていきます。PDFはメモリーがいっぱいになる事はない為、1ページずつ処理をしていきます。ほとんどの書類は10ページ以下ですが、稀に10,000ページ以上の書類も存在する為、このデータを一度にメモリー化するとアプリは直ぐにクラッシュしてしまいます。解析ページ法を使い、ページ内の特徴を抽出して、特性とバウンディングの対象リストが作成されます。 data class CharAndBound( val char: Char, val bound: Rectangle, val color: Int, val fontCode: Int, val rotation: Int ) : IRectangle by bound 急速な—特性解析 急速な特性解析のクラスでのストリップページ法は特性とバウンディング対象のリストを引き戻す事について説明をしました。PFF Boxkからの延長となります。PDFストリームエンジンクラス and listens to the following events: private fun setOperators() { addOperator(ShowText()) addOperator(BeginText()) addOperator(Concatenate()) addOperator(DrawObject()) addOperator(Save()) addOperator(Restore()) addOperator(NextLine()) addOperator(MoveText()) addOperator(MoveTextSetLeading()) addOperator(SetFontAndSize()) addOperator(ShowTextAdjusted()) addOperator(SetTextLeading()) addOperator(SetMatrix()) addOperator(ShowTextLine()) } PFDストリームエンジンは造形文字もあります。 造形文字 早急な特性解析のshowGlyph法では、特性とバウンディング対象の全てのプロパティを計算してくれます。 override fun showGlyph( textMatrix: Matrix, font: PDFont, code: Int, displacement: Vector ) textMatrixパラメーターを使用して造形文字のページ内のローテーションと位置を決定する事が出来ます。又、造形文字の幅や高さを決定するのもバウンディングボックスのフォントを使って決定する事が可能です。いくつかのフォントにはバウンディングボックス情報が無い為、その様な状態が発生した際にはハードコードバウンディングボックスで対応できる事を願います。

Read more →

Join The Mailing List