Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Bill Oneにおける請求書アップロード機能の拡張

Bill Oneにおける請求書アップロード機能の拡張

■イベント


急成長するプロダクトを支える技術とその向き合い方https://sansan.connpass.com/event/291015/

■登壇概要

Bill Oneにおける請求書アップロード機能の拡張

登壇者:技術本部 Bill One Engineering Unit Smart 受領グループ 西村 奈令大





■Sansan 技術本部 採用情報

https://media.sansan-engineering.com/

SansanTech

August 09, 2023
Tweet

More Decks by SansanTech

Other Decks in Technology

Transcript

  1. 写真が入ります ⻄村 奈令⼤ Sansan株式会社 技術本部Bill One Engineering Unit Smart受領グループ 2017年

    Sansan中途入社 EightのWebならびにAndroidアプリ開発を経て2021年に Bill Oneにジョイン 現在はBill Oneにおける請求書受領領域の開発に携わる
  2. アジェンダ - Bill Oneにおける請求書アップロード機能 > 概要 > 拡張前の登録フロー - 電帳法の改正

    - 拡張に必要な要件 > 方針 > 時限付きURLの取得とstorageへの書き込み > 書き込み用バケット > CORS設定 > ライフサイクルならびにretention policyの定義 > 拡張後の登録フロー > 拡張前と拡張後の比較 - おわりに
  3. 電帳法 = 電子帳簿保存法 - 各税法で保存が義務付けられている帳簿・書類を電⼦データで 保存するためのルール等を定めた法律 - 電子データを受け取ったら、電子データのまま保存しなければ ならない -

    紙に印刷しての保存はNG (2022年1月1日施行) 電帳法の改正 参照:https://onl.sc/aSEmBwx 電子データの量がどんどん増える ↓ 30MBを超えるようなサイズの 請求書データが散⾒される可能性⼤
  4. ⽅針 上限が32MBかつ、上限の引き上げができない -> ファイル送信の形式を変更する必要がある いくつか方針を検討 - E2EでHTTP/2への変更 - 分割アップロード -

    署名付きURLによるCloud Storageへの書き込み - Googleが提案しているベストプラクティスである - 参考:https://cloud.google.com/storage/docs/access-control/signed-urls?hl=ja
  5. // backend fun createUploadUrl( … ): URL { ... return

    storage.signUrl( targetBlobInfo, // バケットの情報 15, // URLの有効期限 TimeUnit.MINUTES, // 有効期限の単位 Storage.SignUrlOption.httpMethod(HttpMethod.PUT), // httpメソッド Storage.SignUrlOption.withExtHeaders(extensionHeaders), // ヘッダ情報 Storage.SignUrlOption.withV4Signature(), // 署名の形式指定 Storage.SignUrlOption.signWith(signer) // 署名の際のオプション (クレデンシャル関連) ) } // frontend async uploadToStorage(url: string, pdfFile: File): Promise<Response> { const options = { body: pdfFile, method: "PUT", headers: { "Content-Type": "application/pdf" }, } return await fetch(url, options) } 時限付きURLの取得とstorageへの書き込み
  6. 意図しない経路からのstorage操作を防ぎたい -> CORS(cross origin resource sharing) をバケットに設定したい Bill Oneでは、テナント (≒利用企業)

    ごとにバケットを作成している -> すべてのバケットに対して設定を反映させるのは非現実的 そこで・・・ - 書き込み専⽤のバケットを準備し、直接のアップロードはそちらに 対して⾏う - 上記バケットに配置されたPDFをテナントごとのバケットに移⾏する 書き込み⽤バケット
  7. CORS設定 // cors.json [ { "origin": ["<直接書き込みを許可したいページのURL>"], "responseHeader": ["Content-Type", "Authorization",

    "Content-Length", "User-Agent", "x-goog-resumable"], "method": ["HEAD", "GET", "POST", "PUT", "DELETE", "OPTIONS"], "maxAgeSeconds": 3600 } ] gsutil cors set cors.json gs://<bucket name>
  8. ライフサイクルならびにretention policyの定義 // lifecycle.json { "rule": [ { "action": {"type":

    "Delete"}, "condition": {"age": 1} } ] } gsutil lifecycle set lifecycle.json gs://<bucket name> gsutil retention set 900s gs://<bucket name> - 書き込み用バケットに配置したファイルは最終的にテナント ごとのバケットにコピーされるため、不要となる - 一定時間書き込みを禁止し、上書きが何度も行われないよう にする